diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 237c9ed..e2a410d 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -1,21 +1,21 @@ { "$schema": "https://raw.githubusercontent.com/devcontainers/spec/main/schemas/devContainer.schema.json", "name": "nfcore", - "image": "nfcore/devcontainer:latest", + "image": "nfcore/gitpod:latest", + "remoteUser": "gitpod", + "runArgs": ["--privileged"], - "remoteUser": "root", - "privileged": true, + // Configure tool-specific properties. + "customizations": { + // Configure properties specific to VS Code. + "vscode": { + // Set *default* container specific settings.json values on container create. + "settings": { + "python.defaultInterpreterPath": "/opt/conda/bin/python" + }, - "remoteEnv": { - // Workspace path on the host for mounting with docker-outside-of-docker - "LOCAL_WORKSPACE_FOLDER": "${localWorkspaceFolder}" - }, - - "onCreateCommand": "./.devcontainer/setup.sh", - - "hostRequirements": { - "cpus": 4, - "memory": "16gb", - "storage": "32gb" + // Add the IDs of extensions you want installed when the container is created. + "extensions": ["ms-python.python", "ms-python.vscode-pylance", "nf-core.nf-core-extensionpack"] + } } } diff --git a/.devcontainer/setup.sh b/.devcontainer/setup.sh deleted file mode 100755 index 2ca6343..0000000 --- a/.devcontainer/setup.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/usr/bin/env bash - -# Customise the terminal command prompt -echo "export PROMPT_DIRTRIM=2" >> $HOME/.bashrc -echo "export PS1='\[\e[3;36m\]\w ->\[\e[0m\\] '" >> $HOME/.bashrc -export PROMPT_DIRTRIM=2 -export PS1='\[\e[3;36m\]\w ->\[\e[0m\\] ' - -# Update Nextflow -nextflow self-update - -# Update welcome message -echo "Welcome to the nf-core/deepmutscan devcontainer!" > /usr/local/etc/vscode-dev-containers/first-run-notice.txt diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..6d9b74c --- /dev/null +++ b/.editorconfig @@ -0,0 +1,37 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true +indent_size = 4 +indent_style = space + +[*.{md,yml,yaml,html,css,scss,js}] +indent_size = 2 + +# These files are edited and tested upstream in nf-core/modules +[/modules/nf-core/**] +charset = unset +end_of_line = unset +insert_final_newline = unset +trim_trailing_whitespace = unset +indent_style = unset +[/subworkflows/nf-core/**] +charset = unset +end_of_line = unset +insert_final_newline = unset +trim_trailing_whitespace = unset +indent_style = unset + +[/assets/email*] +indent_size = unset + +# ignore python and markdown +[*.{py,md}] +indent_style = unset + +# ignore ro-crate metadata files +[**/ro-crate-metadata.json] +insert_final_newline = unset diff --git a/.github/workflows/fix-linting.yml b/.github/workflows/fix-linting.yml new file mode 100644 index 0000000..8bda64e --- /dev/null +++ b/.github/workflows/fix-linting.yml @@ -0,0 +1,89 @@ +name: Fix linting from a comment +on: + issue_comment: + types: [created] + +jobs: + fix-linting: + # Only run if comment is on a PR with the main repo, and if it contains the magic keywords + if: > + contains(github.event.comment.html_url, '/pull/') && + contains(github.event.comment.body, '@nf-core-bot fix linting') && + github.repository == 'nf-core/deepmutscan' + runs-on: ubuntu-latest + steps: + # Use the @nf-core-bot token to check out so we can push later + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + with: + token: ${{ secrets.nf_core_bot_auth_token }} + + # indication that the linting is being fixed + - name: React on comment + uses: peter-evans/create-or-update-comment@71345be0265236311c031f5c7866368bd1eff043 # v4 + with: + comment-id: ${{ github.event.comment.id }} + reactions: eyes + + # Action runs on the issue comment, so we don't get the PR by default + # Use the gh cli to check out the PR + - name: Checkout Pull Request + run: gh pr checkout ${{ github.event.issue.number }} + env: + GITHUB_TOKEN: ${{ secrets.nf_core_bot_auth_token }} + + # Install and run pre-commit + - uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5 + with: + python-version: "3.12" + + - name: Install pre-commit + run: pip install pre-commit + + - name: Run pre-commit + id: pre-commit + run: pre-commit run --all-files + continue-on-error: true + + # indication that the linting has finished + - name: react if linting finished succesfully + if: steps.pre-commit.outcome == 'success' + uses: peter-evans/create-or-update-comment@71345be0265236311c031f5c7866368bd1eff043 # v4 + with: + comment-id: ${{ github.event.comment.id }} + reactions: "+1" + + - name: Commit & push changes + id: commit-and-push + if: steps.pre-commit.outcome == 'failure' + run: | + git config user.email "core@nf-co.re" + git config user.name "nf-core-bot" + git config push.default upstream + git add . + git status + git commit -m "[automated] Fix code linting" + git push + + - name: react if linting errors were fixed + id: react-if-fixed + if: steps.commit-and-push.outcome == 'success' + uses: peter-evans/create-or-update-comment@71345be0265236311c031f5c7866368bd1eff043 # v4 + with: + comment-id: ${{ github.event.comment.id }} + reactions: hooray + + - name: react if linting errors were not fixed + if: steps.commit-and-push.outcome == 'failure' + uses: peter-evans/create-or-update-comment@71345be0265236311c031f5c7866368bd1eff043 # v4 + with: + comment-id: ${{ github.event.comment.id }} + reactions: confused + + - name: react if linting errors were not fixed + if: steps.commit-and-push.outcome == 'failure' + uses: peter-evans/create-or-update-comment@71345be0265236311c031f5c7866368bd1eff043 # v4 + with: + issue-number: ${{ github.event.issue.number }} + body: | + @${{ github.actor }} I tried to fix the linting errors, but it didn't work. Please fix them manually. + See [CI log](https://github.com/nf-core/deepmutscan/actions/runs/${{ github.run_id }}) for more details. diff --git a/.github/workflows/template_version_comment.yml b/.github/workflows/template_version_comment.yml new file mode 100644 index 0000000..537529b --- /dev/null +++ b/.github/workflows/template_version_comment.yml @@ -0,0 +1,46 @@ +name: nf-core template version comment +# This workflow is triggered on PRs to check if the pipeline template version matches the latest nf-core version. +# It posts a comment to the PR, even if it comes from a fork. + +on: pull_request_target + +jobs: + template_version: + runs-on: ubuntu-latest + steps: + - name: Check out pipeline code + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + with: + ref: ${{ github.event.pull_request.head.sha }} + + - name: Read template version from .nf-core.yml + uses: nichmor/minimal-read-yaml@v0.0.2 + id: read_yml + with: + config: ${{ github.workspace }}/.nf-core.yml + + - name: Install nf-core + run: | + python -m pip install --upgrade pip + pip install nf-core==${{ steps.read_yml.outputs['nf_core_version'] }} + + - name: Check nf-core outdated + id: nf_core_outdated + run: echo "OUTPUT=$(pip list --outdated | grep nf-core)" >> ${GITHUB_ENV} + + - name: Post nf-core template version comment + uses: mshick/add-pr-comment@b8f338c590a895d50bcbfa6c5859251edc8952fc # v2 + if: | + contains(env.OUTPUT, 'nf-core') + with: + repo-token: ${{ secrets.NF_CORE_BOT_AUTH_TOKEN }} + allow-repeats: false + message: | + > [!WARNING] + > Newer version of the nf-core template is available. + > + > Your pipeline is using an old version of the nf-core template: ${{ steps.read_yml.outputs['nf_core_version'] }}. + > Please update your pipeline to the latest version. + > + > For more documentation on how to update your pipeline, please see the [nf-core documentation](https://github.com/nf-core/tools?tab=readme-ov-file#sync-a-pipeline-with-the-template) and [Synchronisation documentation](https://nf-co.re/docs/contributing/sync). + # diff --git a/.gitignore b/.gitignore index cc2b1a7..0187478 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,10 @@ testing* *.pyc null/ .lineage/ + +# Pipeline outputs +test_results/ +work/ +.nextflow* +.nf-test* +.Rhistory diff --git a/.gitpod.yml b/.gitpod.yml new file mode 100644 index 0000000..83599f6 --- /dev/null +++ b/.gitpod.yml @@ -0,0 +1,10 @@ +image: nfcore/gitpod:latest +tasks: + - name: Update Nextflow and setup pre-commit + command: | + pre-commit install --install-hooks + nextflow self-update + +vscode: + extensions: + - nf-core.nf-core-extensionpack # https://github.com/nf-core/vscode-extensionpack diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index f51e1a2..d237c9a 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -15,7 +15,9 @@ repos: .*ro-crate-metadata.json$| modules/(?!local/).*| subworkflows/(?!local/).*| - .*\.snap$ + .*\.snap$| + assets/test_structure/.*\.pdb$| + assets/variant_effect_inspection_tool/3Dmol-min\.js.* )$ - id: end-of-file-fixer exclude: | @@ -23,7 +25,9 @@ repos: .*ro-crate-metadata.json$| modules/(?!local/).*| subworkflows/(?!local/).*| - .*\.snap$ + .*\.snap$| + assets/test_structure/.*\.pdb$| + assets/variant_effect_inspection_tool/3Dmol-min\.js.* )$ - repo: https://github.com/seqeralabs/nf-lint-pre-commit rev: v0.3.0 diff --git a/.prettierignore b/.prettierignore index 63cde50..0bd1d5e 100644 --- a/.prettierignore +++ b/.prettierignore @@ -12,3 +12,4 @@ bin/ ro-crate-metadata.json modules/nf-core/ subworkflows/nf-core/ +assets/variant_effect_inspection_tool/3Dmol-min.js diff --git a/CITATIONS.md b/CITATIONS.md index 3ea1015..8136400 100644 --- a/CITATIONS.md +++ b/CITATIONS.md @@ -18,6 +18,26 @@ > Ewels P, Magnusson M, Lundin S, Käller M. MultiQC: summarize analysis results for multiple tools and samples in a single report. Bioinformatics. 2016 Oct 1;32(19):3047-8. doi: 10.1093/bioinformatics/btw354. Epub 2016 Jun 16. PubMed PMID: 27312411; PubMed Central PMCID: PMC5039924. +- [SAMtools](https://pubmed.ncbi.nlm.nih.gov/33590861/) + +> Danecek P, Bonfield JK, Liddle J, Marshall J, Ohan V, Pollard MO, Whitwham A, Keane T, McCarthy SA, Davies RM, Li H. Twelve years of SAMtools and BCFtools. Gigascience. 2021 Feb 16;10(2):giab008. doi: 10.1093/gigascience/giab008. PubMed PMID: 33590861; PubMed Central PMCID: PMC7931819. + +- [pysam](https://github.com/pysam-developers/pysam) + +> Heger A, et al. pysam: a Python module for reading, manipulating and writing genomic data sets. (htslib bindings). + +- [Polars](https://github.com/pola-rs/polars) + +> Vink R, et al. Polars: Lightning-fast DataFrame library for Rust and Python. + +- [Biopython](https://pubmed.ncbi.nlm.nih.gov/19304878/) + +> Cock PJ, Antao T, Chang JT, Chapman BA, Cox CJ, Dalke A, Friedberg I, Hamelryck T, Kauff F, Wilczynski B, de Hoon MJ. Biopython: freely available Python tools for computational molecular biology and bioinformatics. Bioinformatics. 2009 Jun 1;25(11):1422-3. doi: 10.1093/bioinformatics/btp163. PubMed PMID: 19304878; PubMed Central PMCID: PMC2682512. + +- [3Dmol.js](https://pubmed.ncbi.nlm.nih.gov/25505090/) + +> Rego N, Koes D. 3Dmol.js: molecular visualization with WebGL. Bioinformatics. 2015 Apr 15;31(8):1322-4. doi: 10.1093/bioinformatics/btu829. PubMed PMID: 25505090; PubMed Central PMCID: PMC4393523. (Bundled in the interactive variant effect inspection tool; BSD-3-Clause.) + ## Software packaging/containerisation tools - [Anaconda](https://anaconda.com) diff --git a/README.md b/README.md index 81bf46a..7a45e11 100644 --- a/README.md +++ b/README.md @@ -5,8 +5,7 @@ -[![Open in GitHub Codespaces](https://img.shields.io/badge/Open_In_GitHub_Codespaces-black?labelColor=grey&logo=github)](https://github.com/codespaces/new/nf-core/deepmutscan) -[![GitHub Actions CI Status](https://github.com/nf-core/deepmutscan/actions/workflows/nf-test.yml/badge.svg)](https://github.com/nf-core/deepmutscan/actions/workflows/nf-test.yml) +[![GitHub Actions CI Status](https://github.com/nf-core/deepmutscan/actions/workflows/ci.yml/badge.svg)](https://github.com/nf-core/deepmutscan/actions/workflows/ci.yml) [![GitHub Actions Linting Status](https://github.com/nf-core/deepmutscan/actions/workflows/linting.yml/badge.svg)](https://github.com/nf-core/deepmutscan/actions/workflows/linting.yml)[![AWS CI](https://img.shields.io/badge/CI%20tests-full%20size-FF9900?labelColor=000000&logo=Amazon%20AWS)](https://nf-co.re/deepmutscan/results)[![Cite with Zenodo](http://img.shields.io/badge/DOI-10.5281/zenodo.XXXXXXX-1073c8?labelColor=000000)](https://doi.org/10.5281/zenodo.XXXXXXX) [![nf-test](https://img.shields.io/badge/unit_tests-nf--test-337ab7.svg)](https://www.nf-test.com) @@ -17,92 +16,110 @@ [![run with singularity](https://img.shields.io/badge/run%20with-singularity-1d355c.svg?labelColor=000000)](https://sylabs.io/docs/) [![Launch on Seqera Platform](https://img.shields.io/badge/Launch%20%F0%9F%9A%80-Seqera%20Platform-%234256e7)](https://cloud.seqera.io/launch?pipeline=https://github.com/nf-core/deepmutscan) -[![Get help on Slack](http://img.shields.io/badge/slack-nf--core%20%23deepmutscan-4A154B?labelColor=000000&logo=slack)](https://nfcore.slack.com/channels/deepmutscan)[![Follow on Bluesky](https://img.shields.io/badge/bluesky-%40nf__core-1185fe?labelColor=000000&logo=bluesky)](https://bsky.app/profile/nf-co.re)[![Follow on Mastodon](https://img.shields.io/badge/mastodon-nf__core-6364ff?labelColor=FFFFFF&logo=mastodon)](https://mstdn.science/@nf_core)[![Watch on YouTube](http://img.shields.io/badge/youtube-nf--core-FF0000?labelColor=000000&logo=youtube)](https://www.youtube.com/c/nf-core) +[![Get help on Slack](http://img.shields.io/badge/slack-nf--core%20%23deepmutscan-4A154B?labelColor=000000&logo=slack)](https://nfcore.slack.com/channels/deepmutscan)[![Follow on Twitter](http://img.shields.io/badge/twitter-%40nf__core-1DA1F2?labelColor=000000&logo=twitter)](https://twitter.com/nf_core)[![Follow on Mastodon](https://img.shields.io/badge/mastodon-nf__core-6364ff?labelColor=FFFFFF&logo=mastodon)](https://mstdn.science/@nf_core)[![Watch on YouTube](http://img.shields.io/badge/youtube-nf--core-FF0000?labelColor=000000&logo=youtube)](https://www.youtube.com/c/nf-core) ## Introduction -**nf-core/deepmutscan** is a bioinformatics pipeline that ... +**nf-core/deepmutscan** is a workflow designed for the analysis of deep mutational scanning (DMS) data. DMS enables researchers to experimentally measure the fitness effects of thousands of genes or gene variants simultaneously, helping to classify disease causing mutants in human and animal populations, to learn the fundamental rules of protein architecture, small-molecule binding, mRNA splicing, viral evolution and many other quantifiable phenotypes. - +While DNA synthesis and sequencing technologies have advanced substantially, long open reading frame (ORF) targets still present a major challenge for DMS studies. Shotgun DNA sequencing can be used to greatly speed up the inference of long ORF mutant fitness landscapes, theoretically at no expense in accuracy. We have designed the `nf-core/deepmutscan` pipeline to unlock the power of shotgun sequencing based DMS studies on long ORFs, to simplify and standardise the complex bioinformatics steps involved in data processing of such experiments – from read alignment to QC reporting and fitness landscape inferences. - -1. Read QC ([`FastQC`](https://www.bioinformatics.babraham.ac.uk/projects/fastqc/))2. Present QC for raw reads ([`MultiQC`](http://multiqc.info/)) +![nf-core/deepmutscan workflow](docs/images/pipeline.png) + +The pipeline is built using [Nextflow](https://www.nextflow.io), a workflow tool to run tasks across multiple compute infrastructures in a very portable manner. It uses Docker/Singularity containers making installation trivial and results highly reproducible. The [Nextflow DSL2](https://www.nextflow.io/docs/latest/dsl2.html) implementation of this pipeline uses one container per process which makes it much easier to maintain and update software dependencies. Where possible, these processes have been submitted to and installed from [nf-core/modules](https://github.com/nf-core/modules) in order to make them available to all nf-core pipelines, and to everyone within the Nextflow community! + +On release, automated continuous integration tests run the pipeline on a full-sized dataset on the AWS cloud infrastructure. This ensures that the pipeline runs on AWS, has sensible resource allocation defaults set to run on real-world datasets, and permits the persistent storage of results to benchmark between pipeline releases and other analysis sources. The results obtained from the full-sized test can be viewed on the [nf-core website](https://nf-co.re/deepmutscan/results). + +## Major features + +- End-to-end analyses of various DMS data +- Modular, three-stage workflow: alignment → QC → error-aware fitness estimation +- Integration with popular statistical fitness estimation tools like [DiMSum](https://github.com/lehner-lab/DiMSum), [Enrich2](https://github.com/FowlerLab/Enrich2), [rosace](https://github.com/pimentellab/rosace/) and [mutscan](https://github.com/fmicompbio/mutscan) +- Support of multiple mutagenesis strategies, e.g. by nicking with degenerate NNK and NNS codons +- Containerisation via Docker, Singularity and Apptainer +- Scalability across HPC and Cloud systems +- Monitoring of CPU, memory, and CO₂ usage + +For more details on the pipeline and on potential future expansions, please consider reading our [usage description](https://nf-co.re/deepmutscan/usage). + +## Step-by-step pipeline summary + +The pipeline processes deep mutational scanning (DMS) sequencing data in several stages: + +1. Alignment of reads to the reference open reading frame (ORF) (`BWA-mem`) +2. Filtering of wildtype and erroneous reads (`samtools view`) +3. Read merging for base error reduction (`vsearch merge`) +4. Mutation counting +5. Single nucleotide variant error correction +6. DMS library quality control +7. Data summarisation across samples +8. Fitness estimation (`DiMSum`, `mutscan`) ## Usage > [!NOTE] > If you are new to Nextflow and nf-core, please refer to [this page](https://nf-co.re/docs/get_started/environment_setup/overview) on how to set-up Nextflow. Make sure to [test your setup](https://nf-co.re/docs/get_started/run-your-first-pipeline) with `-profile test` before running the workflow on actual data. - +Secondly, specify the gene or gene region of interest using a reference FASTA file via `--fasta`. Provide the exact codon coordinates using `--reading_frame`. Now, you can run the pipeline using: - - -```bash +```bash title="example_run.sh" nextflow run nf-core/deepmutscan \ -profile \ - --input samplesheet.csv \ - --outdir + --input ./samplesheet.csv \ + --fasta ./ref.fa \ + --reading_frame 1-300 \ + --outdir ./results ``` -> [!WARNING] -> Please provide pipeline parameters via the CLI or Nextflow `-params-file` option. Custom config files including those provided by the `-c` Nextflow option can be used to provide any configuration _**except for parameters**_; see [docs](https://nf-co.re/docs/running/run-pipelines#using-parameter-files). - -For more details and further functionality, please refer to the [usage documentation](https://nf-co.re/deepmutscan/usage) and the [parameter documentation](https://nf-co.re/deepmutscan/parameters). - ## Pipeline output To see the results of an example test run with a full size dataset refer to the [results](https://nf-co.re/deepmutscan/results) tab on the nf-core website pipeline page. + For more details about the output files and reports, please refer to the [output documentation](https://nf-co.re/deepmutscan/output). -## Credits +## Contributing -nf-core/deepmutscan was originally written by Benjamin Wehnert & Max Stammnitz. +We welcome contributions from the community! -We thank the following people for their extensive assistance in the development of this pipeline: +For technical challenges and feedback on the pipeline, please use our [Github repository](https://github.com/nf-core/deepmutscan). Please open an [issue](https://github.com/nf-core/deepmutscan/issues/new) or [pull request](https://github.com/nf-core/deepmutscan/compare) to: - +- Report bugs or solve data incompatibilities when running `nf-core/deepmutscan` +- Suggest the implementation of new modules for custom DMS workflows +- Help improve this documentation -## Contributions and Support +If you are interested in getting involved as a developer, please consider joining our interactive [`#deepmutscan` Slack channel](https://nfcore.slack.com/channels/deepmutscan) (via [this invite](https://nf-co.re/join/slack)). -If you would like to contribute to this pipeline, please see the [contributing guidelines](docs/CONTRIBUTING.md). +## Credits -For further information or help, don't hesitate to get in touch on the [Slack `#deepmutscan` channel](https://nfcore.slack.com/channels/deepmutscan) (you can join with [this invite](https://nf-co.re/join/slack)). +nf-core/deepmutscan was originally written by [Benjamin Wehnert](https://github.com/BenjaminWehnert1008) and [Max Stammnitz](https://github.com/MaximilianStammnitz) at the [Centre for Genomic Regulation, Barcelona](https://www.crg.eu/), with the generous support of an EMBO Long-term Postdoctoral Fellowship and a Marie Skłodowska-Curie grant by the European Union. + +If you use `nf-core/deepmutscan` in your analyses, please cite: -## Citations +> 📄 Wehnert et al., _bioRxiv_ preprint (coming soon) - - +Please also cite the `nf-core` framework: - +> 📄 Ewels et al., _Nature Biotechnology_, 2020 +> [https://doi.org/10.1038/s41587-020-0439-x](https://doi.org/10.1038/s41587-020-0439-x) + +For further information or help, don't hesitate to get in touch on the [Slack `#deepmutscan` channel](https://nfcore.slack.com/channels/deepmutscan) (you can join with [this invite](https://nf-co.re/join/slack)). -An extensive list of references for the tools used by the pipeline can be found in the [`CITATIONS.md`](CITATIONS.md) file. +## Scientific contact -You can cite the `nf-core` publication as follows: +For scientific discussions around the use of this pipeline (e.g. on experimental design or sequencing data requirements), please feel free to get in touch with us directly: -> **The nf-core framework for community-curated bioinformatics pipelines.** -> -> Philip Ewels, Alexander Peltzer, Sven Fillinger, Harshil Patel, Johannes Alneberg, Andreas Wilm, Maxime Ulysse Garcia, Paolo Di Tommaso & Sven Nahnsen. -> -> _Nat Biotechnol._ 2020 Feb 13. doi: [10.1038/s41587-020-0439-x](https://dx.doi.org/10.1038/s41587-020-0439-x). +- Benjamin Wehnert — wehnertbenjamin@gmail.com +- Maximilian Stammnitz — maximilian.stammnitz@crg.eu diff --git a/assets/error_correction_report/ec_report_template.html b/assets/error_correction_report/ec_report_template.html new file mode 100644 index 0000000..70f4708 --- /dev/null +++ b/assets/error_correction_report/ec_report_template.html @@ -0,0 +1,1839 @@ + + + + + + nf-core/deepmutscan · sequencing-error correction · __SAMPLE__ + + + +
+
+

Sequencing-error correction

+ sample + +
+

+ +
+ + +
+ 1 · COMPARE +

+ Reproducibility of the error signature + +

+
+

+ Where errors recur. The upper triangle scores how strongly two files agree on which variants are error-prone; + the lower triangle shows each pair as a scatter (click to enlarge). Higher agreement means the correction + reflects sequencing chemistry rather than chance. +

+
+
+
+ + +
+ 2 · SELECTION +

+ Raw versus corrected frequency + +

+
+

+ Effect of correction on measured variants, split by input and output libraries. Toggle files; with two or more, + the bars span their spread. +

+
+ + + +
+
+ 3 · DETAIL +

+ One sequencing file in detail + +

+
+

Choose one file; every panel below recomputes for it alone.

+
+
+ +
+
+
+
+
+ Sequencing-error frequency by substitution class + +
+
+
+
+
+ Correction magnitude + +
+
+
+
+
+ +
+

+ Where errors concentrate along the ORF + +

+

Fraction of counts removed at each residue position.

+
+
+ +
+

+ Single-nucleotide substitutions by position + +

+
+ + +
+
+ + + + + + + + + + + + + +
PositionSubstitutionResidue changeClassRaw freqCorrected freq% change
+
+
+ +
nf-core/deepmutscan · sequencing-error correction report
+
+ +
+
+ + + + + diff --git a/assets/multiqc_config.yml b/assets/multiqc_config.yml index 5845dd0..bcfed63 100644 --- a/assets/multiqc_config.yml +++ b/assets/multiqc_config.yml @@ -1,7 +1,5 @@ report_comment: > - This report has been generated by the nf-core/deepmutscan - analysis pipeline. For information about how to interpret these results, please see the - documentation. + This report has been generated by the nf-core/deepmutscan analysis pipeline. For information about how to interpret these results, please see the documentation. report_section_order: "nf-core-deepmutscan-methods-description": order: -1000 diff --git a/assets/samplesheet.csv b/assets/samplesheet.csv index 5f653ab..5b5d503 100644 --- a/assets/samplesheet.csv +++ b/assets/samplesheet.csv @@ -1,3 +1,5 @@ -sample,fastq_1,fastq_2 -SAMPLE_PAIRED_END,/path/to/fastq/files/AEG588A1_S1_L002_R1_001.fastq.gz,/path/to/fastq/files/AEG588A1_S1_L002_R2_001.fastq.gz -SAMPLE_SINGLE_END,/path/to/fastq/files/AEG588A4_S4_L003_R1_001.fastq.gz, +sample,type,replicate,file1,file2 +ORF1,input,1,/reads/forward1.fastq.gz,/reads/reverse1.fastq.gz +ORF1,input,2,/reads/forward2.fastq.gz,/reads/reverse2.fastq.gz +ORF1,output,1,/reads/forward3.fastq.gz,/reads/reverse3.fastq.gz +ORF1,output,2,/reads/forward4.fastq.gz,/reads/reverse4.fastq.gz diff --git a/assets/schema_input.json b/assets/schema_input.json index 656b88f..88afb3a 100644 --- a/assets/schema_input.json +++ b/assets/schema_input.json @@ -9,25 +9,46 @@ "properties": { "sample": { "type": "string", - "pattern": "^\\S+$", - "errorMessage": "Sample name must be provided and cannot contain spaces", + "pattern": "^[^\\s/]+$", + "errorMessage": "Sample name must be provided, cannot contain spaces, and must not include special characters", "meta": ["id"] }, - "fastq_1": { + "file1": { "type": "string", "format": "file-path", "exists": true, - "pattern": "^([\\S\\s]*\\/)?[^\\s\\/]+\\.f(ast)?q\\.gz$", - "errorMessage": "FastQ file for reads 1 must be provided, cannot contain spaces and must have extension '.fq.gz' or '.fastq.gz'" + "pattern": "^\\S+\\.(bam|fastq|fq|fastq\\.gz|fq\\.gz)$", + "allOf": [ + { + "if": { + "pattern": "^\\S+\\.bam$" + }, + "then": { + "pattern": "^\\S+_(pe|se)\\.bam$", + "errorMessage": "If file1 ends with .bam, it must contain '_pe.bam' or '_se.bam', defining paired-end or single-end" + } + } + ], + "errorMessage": "File 1 must be provided, cannot contain spaces, and must have an allowed extension (.bam, .fastq, .fq, .fastq.gz, .fq.gz)" }, - "fastq_2": { - "type": "string", + "file2": { + "type": ["string", "null"], "format": "file-path", "exists": true, - "pattern": "^([\\S\\s]*\\/)?[^\\s\\/]+\\.f(ast)?q\\.gz$", - "errorMessage": "FastQ file for reads 2 cannot contain spaces and must have extension '.fq.gz' or '.fastq.gz'" + "pattern": "^\\S+\\.(fastq|fq|fastq\\.gz|fq\\.gz)$", + "errorMessage": "File 2 must have an allowed extension (.fastq, .fq, .fastq.gz, .fq.gz) or be null for single-end reads" + }, + "type": { + "type": "string", + "enum": ["input", "output", "quality", "wildtype"], + "errorMessage": "Type must be one of: input, output, quality, or wildtype" + }, + "replicate": { + "type": "integer", + "minimum": 1, + "errorMessage": "Replicate must be a positive integer" } }, - "required": ["sample", "fastq_1"] + "required": ["sample", "file1", "type", "replicate"] } } diff --git a/assets/summary_report/summary_report_template.html b/assets/summary_report/summary_report_template.html new file mode 100644 index 0000000..16dbfb1 --- /dev/null +++ b/assets/summary_report/summary_report_template.html @@ -0,0 +1,1948 @@ + + + + + + nf-core/deepmutscan · report · __SAMPLE__ + + + +
+ + report + + + +
+ +
+ +
+
+ +
+ + + + + + + diff --git a/assets/test_structure/GID1A_AFDB.pdb b/assets/test_structure/GID1A_AFDB.pdb new file mode 100644 index 0000000..3d20acc --- /dev/null +++ b/assets/test_structure/GID1A_AFDB.pdb @@ -0,0 +1,2803 @@ +HEADER 01-AUG-25 +TITLE ALPHAFOLD MONOMER V2.0 PREDICTION FOR GIBBERELLIN RECEPTOR GID1A +TITLE 2 (Q9MAA7) +COMPND MOL_ID: 1; +COMPND 2 MOLECULE: GIBBERELLIN RECEPTOR GID1A; +COMPND 3 CHAIN: A +SOURCE MOL_ID: 1; +SOURCE 2 ORGANISM_SCIENTIFIC: ARABIDOPSIS THALIANA; +SOURCE 3 ORGANISM_TAXID: 3702 +REMARK 1 +REMARK 1 REFERENCE 1 +REMARK 1 AUTH JOHN JUMPER, RICHARD EVANS, ALEXANDER PRITZEL, TIM GREEN, +REMARK 1 AUTH 2 MICHAEL FIGURNOV, OLAF RONNEBERGER, KATHRYN TUNYASUVUNAKOOL, +REMARK 1 AUTH 3 RUSS BATES, AUGUSTIN ZIDEK, ANNA POTAPENKO, ALEX BRIDGLAND, +REMARK 1 AUTH 4 CLEMENS MEYER, SIMON A A KOHL, ANDREW J BALLARD, +REMARK 1 AUTH 5 ANDREW COWIE, BERNARDINO ROMERA-PAREDES, STANISLAV NIKOLOV, +REMARK 1 AUTH 6 RISHUB JAIN, JONAS ADLER, TREVOR BACK, STIG PETERSEN, +REMARK 1 AUTH 7 DAVID REIMAN, ELLEN CLANCY, MICHAL ZIELINSKI, +REMARK 1 AUTH 8 MARTIN STEINEGGER, MICHALINA PACHOLSKA, TAMAS BERGHAMMER, +REMARK 1 AUTH 9 DAVID SILVER, ORIOL VINYALS, ANDREW W SENIOR, +REMARK 1 AUTH10 KORAY KAVUKCUOGLU, PUSHMEET KOHLI, DEMIS HASSABIS +REMARK 1 TITL HIGHLY ACCURATE PROTEIN STRUCTURE PREDICTION WITH ALPHAFOLD +REMARK 1 REF NATURE V. 596 583 2021 +REMARK 1 REFN ISSN 0028-0836 +REMARK 1 PMID 34265844 +REMARK 1 DOI 10.1038/s41586-021-03819-2 +REMARK 1 +REMARK 1 DISCLAIMERS +REMARK 1 ALPHAFOLD DATA, COPYRIGHT (2021) DEEPMIND TECHNOLOGIES LIMITED. THE +REMARK 1 INFORMATION PROVIDED IS THEORETICAL MODELLING ONLY AND CAUTION SHOULD +REMARK 1 BE EXERCISED IN ITS USE. IT IS PROVIDED "AS-IS" WITHOUT ANY WARRANTY +REMARK 1 OF ANY KIND, WHETHER EXPRESSED OR IMPLIED. NO WARRANTY IS GIVEN THAT +REMARK 1 USE OF THE INFORMATION SHALL NOT INFRINGE THE RIGHTS OF ANY THIRD +REMARK 1 PARTY. THE INFORMATION IS NOT INTENDED TO BE A SUBSTITUTE FOR +REMARK 1 PROFESSIONAL MEDICAL ADVICE, DIAGNOSIS, OR TREATMENT, AND DOES NOT +REMARK 1 CONSTITUTE MEDICAL OR OTHER PROFESSIONAL ADVICE. IT IS AVAILABLE FOR +REMARK 1 ACADEMIC AND COMMERCIAL PURPOSES, UNDER CC-BY 4.0 LICENCE. +DBREF XXXX A 1 345 UNP Q9MAA7 GID1A_ARATH 1 345 +SEQRES 1 A 345 MET ALA ALA SER ASP GLU VAL ASN LEU ILE GLU SER ARG +SEQRES 2 A 345 THR VAL VAL PRO LEU ASN THR TRP VAL LEU ILE SER ASN +SEQRES 3 A 345 PHE LYS VAL ALA TYR ASN ILE LEU ARG ARG PRO ASP GLY +SEQRES 4 A 345 THR PHE ASN ARG HIS LEU ALA GLU TYR LEU ASP ARG LYS +SEQRES 5 A 345 VAL THR ALA ASN ALA ASN PRO VAL ASP GLY VAL PHE SER +SEQRES 6 A 345 PHE ASP VAL LEU ILE ASP ARG ARG ILE ASN LEU LEU SER +SEQRES 7 A 345 ARG VAL TYR ARG PRO ALA TYR ALA ASP GLN GLU GLN PRO +SEQRES 8 A 345 PRO SER ILE LEU ASP LEU GLU LYS PRO VAL ASP GLY ASP +SEQRES 9 A 345 ILE VAL PRO VAL ILE LEU PHE PHE HIS GLY GLY SER PHE +SEQRES 10 A 345 ALA HIS SER SER ALA ASN SER ALA ILE TYR ASP THR LEU +SEQRES 11 A 345 CYS ARG ARG LEU VAL GLY LEU CYS LYS CYS VAL VAL VAL +SEQRES 12 A 345 SER VAL ASN TYR ARG ARG ALA PRO GLU ASN PRO TYR PRO +SEQRES 13 A 345 CYS ALA TYR ASP ASP GLY TRP ILE ALA LEU ASN TRP VAL +SEQRES 14 A 345 ASN SER ARG SER TRP LEU LYS SER LYS LYS ASP SER LYS +SEQRES 15 A 345 VAL HIS ILE PHE LEU ALA GLY ASP SER SER GLY GLY ASN +SEQRES 16 A 345 ILE ALA HIS ASN VAL ALA LEU ARG ALA GLY GLU SER GLY +SEQRES 17 A 345 ILE ASP VAL LEU GLY ASN ILE LEU LEU ASN PRO MET PHE +SEQRES 18 A 345 GLY GLY ASN GLU ARG THR GLU SER GLU LYS SER LEU ASP +SEQRES 19 A 345 GLY LYS TYR PHE VAL THR VAL ARG ASP ARG ASP TRP TYR +SEQRES 20 A 345 TRP LYS ALA PHE LEU PRO GLU GLY GLU ASP ARG GLU HIS +SEQRES 21 A 345 PRO ALA CYS ASN PRO PHE SER PRO ARG GLY LYS SER LEU +SEQRES 22 A 345 GLU GLY VAL SER PHE PRO LYS SER LEU VAL VAL VAL ALA +SEQRES 23 A 345 GLY LEU ASP LEU ILE ARG ASP TRP GLN LEU ALA TYR ALA +SEQRES 24 A 345 GLU GLY LEU LYS LYS ALA GLY GLN GLU VAL LYS LEU MET +SEQRES 25 A 345 HIS LEU GLU LYS ALA THR VAL GLY PHE TYR LEU LEU PRO +SEQRES 26 A 345 ASN ASN ASN HIS PHE HIS ASN VAL MET ASP GLU ILE SER +SEQRES 27 A 345 ALA PHE VAL ASN ALA GLU CYS +CRYST1 1.000 1.000 1.000 90.00 90.00 90.00 P 1 1 +ORIGX1 1.000000 0.000000 0.000000 0.00000 +ORIGX2 0.000000 1.000000 0.000000 0.00000 +ORIGX3 0.000000 0.000000 1.000000 0.00000 +SCALE1 1.000000 0.000000 0.000000 0.00000 +SCALE2 0.000000 1.000000 0.000000 0.00000 +SCALE3 0.000000 0.000000 1.000000 0.00000 +MODEL 1 +ATOM 1 N MET A 1 32.551 13.921 -16.818 1.00 37.16 N +ATOM 2 CA MET A 1 31.146 13.594 -17.145 1.00 37.16 C +ATOM 3 C MET A 1 30.154 14.268 -16.181 1.00 37.16 C +ATOM 4 CB MET A 1 30.942 12.062 -17.182 1.00 37.16 C +ATOM 5 O MET A 1 29.177 13.650 -15.808 1.00 37.16 O +ATOM 6 CG MET A 1 31.702 11.397 -18.334 1.00 37.16 C +ATOM 7 SD MET A 1 30.973 9.830 -18.876 1.00 37.16 S +ATOM 8 CE MET A 1 31.719 8.639 -17.733 1.00 37.16 C +ATOM 9 N ALA A 2 30.353 15.534 -15.785 1.00 45.28 N +ATOM 10 CA ALA A 2 29.441 16.208 -14.840 1.00 45.28 C +ATOM 11 C ALA A 2 28.253 16.926 -15.520 1.00 45.28 C +ATOM 12 CB ALA A 2 30.273 17.166 -13.979 1.00 45.28 C +ATOM 13 O ALA A 2 27.290 17.287 -14.858 1.00 45.28 O +ATOM 14 N ALA A 3 28.305 17.111 -16.844 1.00 42.53 N +ATOM 15 CA ALA A 3 27.326 17.919 -17.572 1.00 42.53 C +ATOM 16 C ALA A 3 25.971 17.221 -17.822 1.00 42.53 C +ATOM 17 CB ALA A 3 27.979 18.386 -18.880 1.00 42.53 C +ATOM 18 O ALA A 3 24.993 17.909 -18.095 1.00 42.53 O +ATOM 19 N SER A 4 25.878 15.885 -17.740 1.00 52.66 N +ATOM 20 CA SER A 4 24.606 15.179 -17.984 1.00 52.66 C +ATOM 21 C SER A 4 23.632 15.298 -16.817 1.00 52.66 C +ATOM 22 CB SER A 4 24.838 13.695 -18.294 1.00 52.66 C +ATOM 23 O SER A 4 22.430 15.450 -17.023 1.00 52.66 O +ATOM 24 OG SER A 4 25.556 13.043 -17.259 1.00 52.66 O +ATOM 25 N ASP A 5 24.142 15.239 -15.589 1.00 48.25 N +ATOM 26 CA ASP A 5 23.291 15.106 -14.407 1.00 48.25 C +ATOM 27 C ASP A 5 22.691 16.450 -13.990 1.00 48.25 C +ATOM 28 CB ASP A 5 24.075 14.440 -13.267 1.00 48.25 C +ATOM 29 O ASP A 5 21.531 16.502 -13.586 1.00 48.25 O +ATOM 30 CG ASP A 5 24.444 12.978 -13.556 1.00 48.25 C +ATOM 31 OD1 ASP A 5 24.081 12.474 -14.648 1.00 48.25 O +ATOM 32 OD2 ASP A 5 25.125 12.394 -12.686 1.00 48.25 O +ATOM 33 N GLU A 6 23.431 17.553 -14.148 1.00 51.66 N +ATOM 34 CA GLU A 6 22.931 18.891 -13.811 1.00 51.66 C +ATOM 35 C GLU A 6 21.745 19.312 -14.685 1.00 51.66 C +ATOM 36 CB GLU A 6 24.048 19.939 -13.908 1.00 51.66 C +ATOM 37 O GLU A 6 20.742 19.789 -14.152 1.00 51.66 O +ATOM 38 CG GLU A 6 24.992 19.887 -12.698 1.00 51.66 C +ATOM 39 CD GLU A 6 25.985 21.062 -12.668 1.00 51.66 C +ATOM 40 OE1 GLU A 6 26.514 21.327 -11.564 1.00 51.66 O +ATOM 41 OE2 GLU A 6 26.211 21.684 -13.730 1.00 51.66 O +ATOM 42 N VAL A 7 21.811 19.089 -16.005 1.00 47.53 N +ATOM 43 CA VAL A 7 20.706 19.420 -16.926 1.00 47.53 C +ATOM 44 C VAL A 7 19.460 18.598 -16.587 1.00 47.53 C +ATOM 45 CB VAL A 7 21.127 19.226 -18.397 1.00 47.53 C +ATOM 46 O VAL A 7 18.372 19.157 -16.444 1.00 47.53 O +ATOM 47 CG1 VAL A 7 19.978 19.530 -19.366 1.00 47.53 C +ATOM 48 CG2 VAL A 7 22.294 20.156 -18.759 1.00 47.53 C +ATOM 49 N ASN A 8 19.631 17.293 -16.341 1.00 58.84 N +ATOM 50 CA ASN A 8 18.542 16.404 -15.927 1.00 58.84 C +ATOM 51 C ASN A 8 17.887 16.863 -14.607 1.00 58.84 C +ATOM 52 CB ASN A 8 19.113 14.982 -15.776 1.00 58.84 C +ATOM 53 O ASN A 8 16.663 16.823 -14.458 1.00 58.84 O +ATOM 54 CG ASN A 8 19.412 14.281 -17.090 1.00 58.84 C +ATOM 55 ND2 ASN A 8 20.333 13.347 -17.094 1.00 58.84 N +ATOM 56 OD1 ASN A 8 18.758 14.455 -18.102 1.00 58.84 O +ATOM 57 N LEU A 9 18.690 17.328 -13.643 1.00 60.03 N +ATOM 58 CA LEU A 9 18.189 17.832 -12.364 1.00 60.03 C +ATOM 59 C LEU A 9 17.413 19.145 -12.521 1.00 60.03 C +ATOM 60 CB LEU A 9 19.357 18.003 -11.378 1.00 60.03 C +ATOM 61 O LEU A 9 16.423 19.339 -11.817 1.00 60.03 O +ATOM 62 CG LEU A 9 19.898 16.680 -10.807 1.00 60.03 C +ATOM 63 CD1 LEU A 9 21.210 16.952 -10.067 1.00 60.03 C +ATOM 64 CD2 LEU A 9 18.924 16.037 -9.815 1.00 60.03 C +ATOM 65 N ILE A 10 17.812 20.033 -13.434 1.00 62.09 N +ATOM 66 CA ILE A 10 17.093 21.291 -13.683 1.00 62.09 C +ATOM 67 C ILE A 10 15.707 21.014 -14.277 1.00 62.09 C +ATOM 68 CB ILE A 10 17.938 22.244 -14.558 1.00 62.09 C +ATOM 69 O ILE A 10 14.724 21.551 -13.768 1.00 62.09 O +ATOM 70 CG1 ILE A 10 19.169 22.736 -13.762 1.00 62.09 C +ATOM 71 CG2 ILE A 10 17.108 23.458 -15.023 1.00 62.09 C +ATOM 72 CD1 ILE A 10 20.241 23.403 -14.634 1.00 62.09 C +ATOM 73 N GLU A 11 15.604 20.140 -15.282 1.00 66.12 N +ATOM 74 CA GLU A 11 14.314 19.758 -15.878 1.00 66.12 C +ATOM 75 C GLU A 11 13.386 19.103 -14.851 1.00 66.12 C +ATOM 76 CB GLU A 11 14.532 18.789 -17.046 1.00 66.12 C +ATOM 77 O GLU A 11 12.216 19.458 -14.736 1.00 66.12 O +ATOM 78 CG GLU A 11 15.164 19.466 -18.270 1.00 66.12 C +ATOM 79 CD GLU A 11 15.323 18.507 -19.462 1.00 66.12 C +ATOM 80 OE1 GLU A 11 15.684 19.013 -20.548 1.00 66.12 O +ATOM 81 OE2 GLU A 11 15.079 17.288 -19.293 1.00 66.12 O +ATOM 82 N SER A 12 13.906 18.195 -14.025 1.00 67.88 N +ATOM 83 CA SER A 12 13.073 17.517 -13.025 1.00 67.88 C +ATOM 84 C SER A 12 12.460 18.477 -11.987 1.00 67.88 C +ATOM 85 CB SER A 12 13.883 16.405 -12.363 1.00 67.88 C +ATOM 86 O SER A 12 11.333 18.261 -11.531 1.00 67.88 O +ATOM 87 OG SER A 12 14.937 16.960 -11.608 1.00 67.88 O +ATOM 88 N ARG A 13 13.149 19.584 -11.662 1.00 67.12 N +ATOM 89 CA ARG A 13 12.662 20.623 -10.736 1.00 67.12 C +ATOM 90 C ARG A 13 11.535 21.478 -11.311 1.00 67.12 C +ATOM 91 CB ARG A 13 13.818 21.539 -10.316 1.00 67.12 C +ATOM 92 O ARG A 13 10.814 22.088 -10.527 1.00 67.12 O +ATOM 93 CG ARG A 13 14.783 20.867 -9.337 1.00 67.12 C +ATOM 94 CD ARG A 13 15.946 21.825 -9.068 1.00 67.12 C +ATOM 95 NE ARG A 13 16.982 21.200 -8.230 1.00 67.12 N +ATOM 96 NH1 ARG A 13 18.138 23.118 -7.722 1.00 67.12 N +ATOM 97 NH2 ARG A 13 18.910 21.148 -7.017 1.00 67.12 N +ATOM 98 CZ ARG A 13 17.998 21.822 -7.660 1.00 67.12 C +ATOM 99 N THR A 14 11.377 21.549 -12.635 1.00 75.06 N +ATOM 100 CA THR A 14 10.277 22.308 -13.259 1.00 75.06 C +ATOM 101 C THR A 14 8.999 21.482 -13.390 1.00 75.06 C +ATOM 102 CB THR A 14 10.671 22.895 -14.623 1.00 75.06 C +ATOM 103 O THR A 14 7.915 22.052 -13.487 1.00 75.06 O +ATOM 104 CG2 THR A 14 11.909 23.789 -14.550 1.00 75.06 C +ATOM 105 OG1 THR A 14 10.944 21.877 -15.546 1.00 75.06 O +ATOM 106 N VAL A 15 9.107 20.149 -13.343 1.00 82.19 N +ATOM 107 CA VAL A 15 7.970 19.222 -13.448 1.00 82.19 C +ATOM 108 C VAL A 15 7.313 18.954 -12.091 1.00 82.19 C +ATOM 109 CB VAL A 15 8.427 17.904 -14.104 1.00 82.19 C +ATOM 110 O VAL A 15 6.089 18.864 -11.997 1.00 82.19 O +ATOM 111 CG1 VAL A 15 7.275 16.904 -14.237 1.00 82.19 C +ATOM 112 CG2 VAL A 15 8.987 18.142 -15.514 1.00 82.19 C +ATOM 113 N VAL A 16 8.105 18.818 -11.023 1.00 83.50 N +ATOM 114 CA VAL A 16 7.589 18.466 -9.691 1.00 83.50 C +ATOM 115 C VAL A 16 7.214 19.724 -8.904 1.00 83.50 C +ATOM 116 CB VAL A 16 8.606 17.646 -8.885 1.00 83.50 C +ATOM 117 O VAL A 16 8.070 20.589 -8.719 1.00 83.50 O +ATOM 118 CG1 VAL A 16 8.007 17.134 -7.567 1.00 83.50 C +ATOM 119 CG2 VAL A 16 9.062 16.405 -9.647 1.00 83.50 C +ATOM 120 N PRO A 17 5.996 19.816 -8.334 1.00 86.50 N +ATOM 121 CA PRO A 17 5.652 20.900 -7.418 1.00 86.50 C +ATOM 122 C PRO A 17 6.669 21.020 -6.275 1.00 86.50 C +ATOM 123 CB PRO A 17 4.253 20.563 -6.888 1.00 86.50 C +ATOM 124 O PRO A 17 7.023 20.016 -5.653 1.00 86.50 O +ATOM 125 CG PRO A 17 3.649 19.702 -7.996 1.00 86.50 C +ATOM 126 CD PRO A 17 4.854 18.934 -8.534 1.00 86.50 C +ATOM 127 N LEU A 18 7.099 22.244 -5.949 1.00 88.38 N +ATOM 128 CA LEU A 18 8.189 22.497 -4.993 1.00 88.38 C +ATOM 129 C LEU A 18 8.002 21.770 -3.650 1.00 88.38 C +ATOM 130 CB LEU A 18 8.319 24.019 -4.791 1.00 88.38 C +ATOM 131 O LEU A 18 8.936 21.148 -3.151 1.00 88.38 O +ATOM 132 CG LEU A 18 9.426 24.444 -3.803 1.00 88.38 C +ATOM 133 CD1 LEU A 18 10.818 24.013 -4.270 1.00 88.38 C +ATOM 134 CD2 LEU A 18 9.418 25.964 -3.648 1.00 88.38 C +ATOM 135 N ASN A 19 6.790 21.782 -3.089 1.00 88.56 N +ATOM 136 CA ASN A 19 6.497 21.109 -1.817 1.00 88.56 C +ATOM 137 C ASN A 19 6.683 19.585 -1.905 1.00 88.56 C +ATOM 138 CB ASN A 19 5.065 21.465 -1.375 1.00 88.56 C +ATOM 139 O ASN A 19 7.212 18.974 -0.977 1.00 88.56 O +ATOM 140 CG ASN A 19 4.902 22.929 -1.002 1.00 88.56 C +ATOM 141 ND2 ASN A 19 3.688 23.419 -0.931 1.00 88.56 N +ATOM 142 OD1 ASN A 19 5.853 23.654 -0.782 1.00 88.56 O +ATOM 143 N THR A 20 6.295 18.975 -3.030 1.00 88.31 N +ATOM 144 CA THR A 20 6.513 17.544 -3.290 1.00 88.31 C +ATOM 145 C THR A 20 8.000 17.258 -3.472 1.00 88.31 C +ATOM 146 CB THR A 20 5.724 17.079 -4.525 1.00 88.31 C +ATOM 147 O THR A 20 8.514 16.309 -2.883 1.00 88.31 O +ATOM 148 CG2 THR A 20 5.820 15.574 -4.758 1.00 88.31 C +ATOM 149 OG1 THR A 20 4.357 17.377 -4.356 1.00 88.31 O +ATOM 150 N TRP A 21 8.716 18.111 -4.212 1.00 88.81 N +ATOM 151 CA TRP A 21 10.160 17.979 -4.391 1.00 88.81 C +ATOM 152 C TRP A 21 10.905 18.020 -3.054 1.00 88.81 C +ATOM 153 CB TRP A 21 10.680 19.066 -5.342 1.00 88.81 C +ATOM 154 O TRP A 21 11.747 17.160 -2.796 1.00 88.81 O +ATOM 155 CG TRP A 21 12.148 18.965 -5.617 1.00 88.81 C +ATOM 156 CD1 TRP A 21 13.135 19.398 -4.800 1.00 88.81 C +ATOM 157 CD2 TRP A 21 12.820 18.322 -6.740 1.00 88.81 C +ATOM 158 CE2 TRP A 21 14.227 18.355 -6.507 1.00 88.81 C +ATOM 159 CE3 TRP A 21 12.386 17.676 -7.913 1.00 88.81 C +ATOM 160 NE1 TRP A 21 14.362 19.030 -5.314 1.00 88.81 N +ATOM 161 CH2 TRP A 21 14.673 17.097 -8.518 1.00 88.81 C +ATOM 162 CZ2 TRP A 21 15.150 17.755 -7.374 1.00 88.81 C +ATOM 163 CZ3 TRP A 21 13.296 17.065 -8.787 1.00 88.81 C +ATOM 164 N VAL A 22 10.580 18.980 -2.183 1.00 90.19 N +ATOM 165 CA VAL A 22 11.181 19.107 -0.846 1.00 90.19 C +ATOM 166 C VAL A 22 10.861 17.885 0.017 1.00 90.19 C +ATOM 167 CB VAL A 22 10.725 20.414 -0.163 1.00 90.19 C +ATOM 168 O VAL A 22 11.774 17.307 0.609 1.00 90.19 O +ATOM 169 CG1 VAL A 22 11.168 20.494 1.305 1.00 90.19 C +ATOM 170 CG2 VAL A 22 11.319 21.635 -0.879 1.00 90.19 C +ATOM 171 N LEU A 23 9.595 17.451 0.054 1.00 90.62 N +ATOM 172 CA LEU A 23 9.169 16.282 0.828 1.00 90.62 C +ATOM 173 C LEU A 23 9.942 15.021 0.418 1.00 90.62 C +ATOM 174 CB LEU A 23 7.647 16.096 0.656 1.00 90.62 C +ATOM 175 O LEU A 23 10.543 14.358 1.263 1.00 90.62 O +ATOM 176 CG LEU A 23 7.076 14.860 1.377 1.00 90.62 C +ATOM 177 CD1 LEU A 23 7.238 14.962 2.895 1.00 90.62 C +ATOM 178 CD2 LEU A 23 5.589 14.705 1.060 1.00 90.62 C +ATOM 179 N ILE A 24 9.967 14.708 -0.879 1.00 90.00 N +ATOM 180 CA ILE A 24 10.604 13.483 -1.374 1.00 90.00 C +ATOM 181 C ILE A 24 12.133 13.585 -1.300 1.00 90.00 C +ATOM 182 CB ILE A 24 10.094 13.120 -2.786 1.00 90.00 C +ATOM 183 O ILE A 24 12.794 12.593 -0.995 1.00 90.00 O +ATOM 184 CG1 ILE A 24 8.556 12.949 -2.783 1.00 90.00 C +ATOM 185 CG2 ILE A 24 10.752 11.804 -3.239 1.00 90.00 C +ATOM 186 CD1 ILE A 24 7.968 12.689 -4.171 1.00 90.00 C +ATOM 187 N SER A 25 12.717 14.773 -1.486 1.00 90.50 N +ATOM 188 CA SER A 25 14.160 14.979 -1.292 1.00 90.50 C +ATOM 189 C SER A 25 14.590 14.687 0.147 1.00 90.50 C +ATOM 190 CB SER A 25 14.582 16.402 -1.662 1.00 90.50 C +ATOM 191 O SER A 25 15.621 14.044 0.352 1.00 90.50 O +ATOM 192 OG SER A 25 14.370 16.631 -3.038 1.00 90.50 O +ATOM 193 N ASN A 26 13.786 15.076 1.142 1.00 91.38 N +ATOM 194 CA ASN A 26 14.047 14.732 2.542 1.00 91.38 C +ATOM 195 C ASN A 26 13.986 13.215 2.773 1.00 91.38 C +ATOM 196 CB ASN A 26 13.063 15.486 3.450 1.00 91.38 C +ATOM 197 O ASN A 26 14.878 12.668 3.425 1.00 91.38 O +ATOM 198 CG ASN A 26 13.403 16.958 3.590 1.00 91.38 C +ATOM 199 ND2 ASN A 26 12.427 17.788 3.871 1.00 91.38 N +ATOM 200 OD1 ASN A 26 14.540 17.383 3.483 1.00 91.38 O +ATOM 201 N PHE A 27 13.005 12.518 2.184 1.00 92.12 N +ATOM 202 CA PHE A 27 12.968 11.052 2.230 1.00 92.12 C +ATOM 203 C PHE A 27 14.186 10.425 1.564 1.00 92.12 C +ATOM 204 CB PHE A 27 11.684 10.498 1.600 1.00 92.12 C +ATOM 205 O PHE A 27 14.791 9.535 2.148 1.00 92.12 O +ATOM 206 CG PHE A 27 10.402 10.857 2.320 1.00 92.12 C +ATOM 207 CD1 PHE A 27 10.364 10.923 3.728 1.00 92.12 C +ATOM 208 CD2 PHE A 27 9.225 11.082 1.580 1.00 92.12 C +ATOM 209 CE1 PHE A 27 9.174 11.256 4.388 1.00 92.12 C +ATOM 210 CE2 PHE A 27 8.029 11.403 2.243 1.00 92.12 C +ATOM 211 CZ PHE A 27 8.014 11.504 3.645 1.00 92.12 C +ATOM 212 N LYS A 28 14.607 10.916 0.395 1.00 91.25 N +ATOM 213 CA LYS A 28 15.803 10.423 -0.300 1.00 91.25 C +ATOM 214 C LYS A 28 17.047 10.531 0.583 1.00 91.25 C +ATOM 215 CB LYS A 28 15.966 11.184 -1.626 1.00 91.25 C +ATOM 216 O LYS A 28 17.787 9.558 0.715 1.00 91.25 O +ATOM 217 CG LYS A 28 17.151 10.658 -2.451 1.00 91.25 C +ATOM 218 CD LYS A 28 17.311 11.455 -3.752 1.00 91.25 C +ATOM 219 CE LYS A 28 18.440 10.847 -4.596 1.00 91.25 C +ATOM 220 NZ LYS A 28 18.562 11.493 -5.930 1.00 91.25 N +ATOM 221 N VAL A 29 17.270 11.688 1.211 1.00 92.88 N +ATOM 222 CA VAL A 29 18.415 11.895 2.114 1.00 92.88 C +ATOM 223 C VAL A 29 18.339 10.948 3.310 1.00 92.88 C +ATOM 224 CB VAL A 29 18.515 13.365 2.564 1.00 92.88 C +ATOM 225 O VAL A 29 19.307 10.231 3.567 1.00 92.88 O +ATOM 226 CG1 VAL A 29 19.594 13.569 3.638 1.00 92.88 C +ATOM 227 CG2 VAL A 29 18.884 14.262 1.375 1.00 92.88 C +ATOM 228 N ALA A 30 17.190 10.886 3.992 1.00 93.94 N +ATOM 229 CA ALA A 30 16.987 9.984 5.122 1.00 93.94 C +ATOM 230 C ALA A 30 17.223 8.520 4.713 1.00 93.94 C +ATOM 231 CB ALA A 30 15.580 10.206 5.691 1.00 93.94 C +ATOM 232 O ALA A 30 18.037 7.823 5.304 1.00 93.94 O +ATOM 233 N TYR A 31 16.600 8.053 3.636 1.00 94.69 N +ATOM 234 CA TYR A 31 16.704 6.666 3.182 1.00 94.69 C +ATOM 235 C TYR A 31 18.125 6.275 2.766 1.00 94.69 C +ATOM 236 CB TYR A 31 15.725 6.446 2.026 1.00 94.69 C +ATOM 237 O TYR A 31 18.502 5.108 2.895 1.00 94.69 O +ATOM 238 CG TYR A 31 14.240 6.486 2.357 1.00 94.69 C +ATOM 239 CD1 TYR A 31 13.754 6.744 3.661 1.00 94.69 C +ATOM 240 CD2 TYR A 31 13.324 6.237 1.318 1.00 94.69 C +ATOM 241 CE1 TYR A 31 12.372 6.744 3.921 1.00 94.69 C +ATOM 242 CE2 TYR A 31 11.942 6.226 1.577 1.00 94.69 C +ATOM 243 OH TYR A 31 10.127 6.431 3.110 1.00 94.69 O +ATOM 244 CZ TYR A 31 11.464 6.473 2.878 1.00 94.69 C +ATOM 245 N ASN A 32 18.929 7.226 2.288 1.00 92.88 N +ATOM 246 CA ASN A 32 20.327 6.986 1.942 1.00 92.88 C +ATOM 247 C ASN A 32 21.214 6.800 3.175 1.00 92.88 C +ATOM 248 CB ASN A 32 20.823 8.125 1.038 1.00 92.88 C +ATOM 249 O ASN A 32 22.059 5.910 3.170 1.00 92.88 O +ATOM 250 CG ASN A 32 20.259 8.023 -0.366 1.00 92.88 C +ATOM 251 ND2 ASN A 32 20.500 9.000 -1.206 1.00 92.88 N +ATOM 252 OD1 ASN A 32 19.637 7.046 -0.746 1.00 92.88 O +ATOM 253 N ILE A 33 21.016 7.580 4.242 1.00 95.38 N +ATOM 254 CA ILE A 33 21.836 7.455 5.461 1.00 95.38 C +ATOM 255 C ILE A 33 21.488 6.215 6.301 1.00 95.38 C +ATOM 256 CB ILE A 33 21.806 8.745 6.307 1.00 95.38 C +ATOM 257 O ILE A 33 22.294 5.795 7.132 1.00 95.38 O +ATOM 258 CG1 ILE A 33 20.398 9.076 6.838 1.00 95.38 C +ATOM 259 CG2 ILE A 33 22.380 9.916 5.487 1.00 95.38 C +ATOM 260 CD1 ILE A 33 20.370 10.186 7.890 1.00 95.38 C +ATOM 261 N LEU A 34 20.310 5.621 6.073 1.00 97.50 N +ATOM 262 CA LEU A 34 19.822 4.432 6.781 1.00 97.50 C +ATOM 263 C LEU A 34 20.388 3.116 6.244 1.00 97.50 C +ATOM 264 CB LEU A 34 18.285 4.404 6.705 1.00 97.50 C +ATOM 265 O LEU A 34 20.367 2.117 6.959 1.00 97.50 O +ATOM 266 CG LEU A 34 17.600 5.548 7.468 1.00 97.50 C +ATOM 267 CD1 LEU A 34 16.100 5.547 7.176 1.00 97.50 C +ATOM 268 CD2 LEU A 34 17.849 5.474 8.962 1.00 97.50 C +ATOM 269 N ARG A 35 20.884 3.102 5.004 1.00 95.75 N +ATOM 270 CA ARG A 35 21.355 1.895 4.319 1.00 95.75 C +ATOM 271 C ARG A 35 22.866 1.888 4.197 1.00 95.75 C +ATOM 272 CB ARG A 35 20.718 1.801 2.932 1.00 95.75 C +ATOM 273 O ARG A 35 23.490 2.927 3.973 1.00 95.75 O +ATOM 274 CG ARG A 35 19.205 1.582 3.027 1.00 95.75 C +ATOM 275 CD ARG A 35 18.590 1.550 1.635 1.00 95.75 C +ATOM 276 NE ARG A 35 18.659 2.870 0.995 1.00 95.75 N +ATOM 277 NH1 ARG A 35 19.006 2.171 -1.174 1.00 95.75 N +ATOM 278 NH2 ARG A 35 19.004 4.316 -0.715 1.00 95.75 N +ATOM 279 CZ ARG A 35 18.883 3.104 -0.279 1.00 95.75 C +ATOM 280 N ARG A 36 23.457 0.701 4.282 1.00 93.69 N +ATOM 281 CA ARG A 36 24.890 0.494 4.069 1.00 93.69 C +ATOM 282 C ARG A 36 25.147 -0.442 2.883 1.00 93.69 C +ATOM 283 CB ARG A 36 25.510 -0.046 5.360 1.00 93.69 C +ATOM 284 O ARG A 36 24.294 -1.271 2.555 1.00 93.69 O +ATOM 285 CG ARG A 36 25.356 0.850 6.598 1.00 93.69 C +ATOM 286 CD ARG A 36 25.957 2.245 6.393 1.00 93.69 C +ATOM 287 NE ARG A 36 25.836 3.064 7.611 1.00 93.69 N +ATOM 288 NH1 ARG A 36 26.206 5.104 6.627 1.00 93.69 N +ATOM 289 NH2 ARG A 36 25.857 4.989 8.827 1.00 93.69 N +ATOM 290 CZ ARG A 36 25.963 4.376 7.682 1.00 93.69 C +ATOM 291 N PRO A 37 26.310 -0.319 2.211 1.00 89.31 N +ATOM 292 CA PRO A 37 26.663 -1.191 1.089 1.00 89.31 C +ATOM 293 C PRO A 37 26.723 -2.685 1.434 1.00 89.31 C +ATOM 294 CB PRO A 37 28.030 -0.698 0.605 1.00 89.31 C +ATOM 295 O PRO A 37 26.521 -3.508 0.549 1.00 89.31 O +ATOM 296 CG PRO A 37 28.058 0.767 1.031 1.00 89.31 C +ATOM 297 CD PRO A 37 27.306 0.738 2.356 1.00 89.31 C +ATOM 298 N ASP A 38 26.966 -3.032 2.700 1.00 91.00 N +ATOM 299 CA ASP A 38 26.983 -4.413 3.205 1.00 91.00 C +ATOM 300 C ASP A 38 25.578 -5.030 3.374 1.00 91.00 C +ATOM 301 CB ASP A 38 27.793 -4.464 4.515 1.00 91.00 C +ATOM 302 O ASP A 38 25.460 -6.192 3.748 1.00 91.00 O +ATOM 303 CG ASP A 38 27.175 -3.664 5.669 1.00 91.00 C +ATOM 304 OD1 ASP A 38 26.068 -3.104 5.491 1.00 91.00 O +ATOM 305 OD2 ASP A 38 27.837 -3.522 6.712 1.00 91.00 O +ATOM 306 N GLY A 39 24.513 -4.276 3.074 1.00 89.31 N +ATOM 307 CA GLY A 39 23.124 -4.718 3.214 1.00 89.31 C +ATOM 308 C GLY A 39 22.503 -4.416 4.579 1.00 89.31 C +ATOM 309 O GLY A 39 21.310 -4.645 4.759 1.00 89.31 O +ATOM 310 N THR A 40 23.257 -3.853 5.529 1.00 94.31 N +ATOM 311 CA THR A 40 22.709 -3.487 6.841 1.00 94.31 C +ATOM 312 C THR A 40 21.812 -2.245 6.771 1.00 94.31 C +ATOM 313 CB THR A 40 23.787 -3.334 7.926 1.00 94.31 C +ATOM 314 O THR A 40 22.010 -1.333 5.956 1.00 94.31 O +ATOM 315 CG2 THR A 40 24.585 -4.617 8.146 1.00 94.31 C +ATOM 316 OG1 THR A 40 24.686 -2.297 7.635 1.00 94.31 O +ATOM 317 N PHE A 41 20.807 -2.216 7.654 1.00 97.94 N +ATOM 318 CA PHE A 41 19.811 -1.152 7.769 1.00 97.94 C +ATOM 319 C PHE A 41 19.744 -0.619 9.203 1.00 97.94 C +ATOM 320 CB PHE A 41 18.443 -1.693 7.349 1.00 97.94 C +ATOM 321 O PHE A 41 19.549 -1.379 10.148 1.00 97.94 O +ATOM 322 CG PHE A 41 17.390 -0.616 7.214 1.00 97.94 C +ATOM 323 CD1 PHE A 41 16.512 -0.333 8.275 1.00 97.94 C +ATOM 324 CD2 PHE A 41 17.305 0.120 6.023 1.00 97.94 C +ATOM 325 CE1 PHE A 41 15.568 0.700 8.143 1.00 97.94 C +ATOM 326 CE2 PHE A 41 16.346 1.132 5.882 1.00 97.94 C +ATOM 327 CZ PHE A 41 15.477 1.429 6.946 1.00 97.94 C +ATOM 328 N ASN A 42 19.872 0.695 9.391 1.00 97.94 N +ATOM 329 CA ASN A 42 19.769 1.304 10.716 1.00 97.94 C +ATOM 330 C ASN A 42 18.303 1.574 11.093 1.00 97.94 C +ATOM 331 CB ASN A 42 20.676 2.541 10.799 1.00 97.94 C +ATOM 332 O ASN A 42 17.823 2.704 11.000 1.00 97.94 O +ATOM 333 CG ASN A 42 20.804 3.057 12.227 1.00 97.94 C +ATOM 334 ND2 ASN A 42 21.839 3.812 12.508 1.00 97.94 N +ATOM 335 OD1 ASN A 42 19.999 2.803 13.110 1.00 97.94 O +ATOM 336 N ARG A 43 17.595 0.536 11.553 1.00 97.06 N +ATOM 337 CA ARG A 43 16.190 0.624 11.989 1.00 97.06 C +ATOM 338 C ARG A 43 15.943 1.679 13.047 1.00 97.06 C +ATOM 339 CB ARG A 43 15.763 -0.731 12.556 1.00 97.06 C +ATOM 340 O ARG A 43 14.984 2.430 12.940 1.00 97.06 O +ATOM 341 CG ARG A 43 14.283 -0.838 12.973 1.00 97.06 C +ATOM 342 CD ARG A 43 13.292 -0.454 11.875 1.00 97.06 C +ATOM 343 NE ARG A 43 13.452 -1.312 10.690 1.00 97.06 N +ATOM 344 NH1 ARG A 43 12.315 0.033 9.256 1.00 97.06 N +ATOM 345 NH2 ARG A 43 13.150 -1.922 8.540 1.00 97.06 N +ATOM 346 CZ ARG A 43 12.968 -1.067 9.497 1.00 97.06 C +ATOM 347 N HIS A 44 16.799 1.738 14.063 1.00 97.31 N +ATOM 348 CA HIS A 44 16.622 2.666 15.174 1.00 97.31 C +ATOM 349 C HIS A 44 16.631 4.119 14.682 1.00 97.31 C +ATOM 350 CB HIS A 44 17.719 2.409 16.210 1.00 97.31 C +ATOM 351 O HIS A 44 15.750 4.903 15.029 1.00 97.31 O +ATOM 352 CG HIS A 44 17.600 3.324 17.398 1.00 97.31 C +ATOM 353 CD2 HIS A 44 16.916 3.074 18.556 1.00 97.31 C +ATOM 354 ND1 HIS A 44 18.103 4.602 17.490 1.00 97.31 N +ATOM 355 CE1 HIS A 44 17.730 5.109 18.675 1.00 97.31 C +ATOM 356 NE2 HIS A 44 17.011 4.210 19.362 1.00 97.31 N +ATOM 357 N LEU A 45 17.585 4.470 13.813 1.00 97.88 N +ATOM 358 CA LEU A 45 17.608 5.788 13.185 1.00 97.88 C +ATOM 359 C LEU A 45 16.412 5.991 12.241 1.00 97.88 C +ATOM 360 CB LEU A 45 18.957 5.977 12.475 1.00 97.88 C +ATOM 361 O LEU A 45 15.879 7.094 12.183 1.00 97.88 O +ATOM 362 CG LEU A 45 19.160 7.370 11.853 1.00 97.88 C +ATOM 363 CD1 LEU A 45 19.098 8.492 12.892 1.00 97.88 C +ATOM 364 CD2 LEU A 45 20.529 7.430 11.172 1.00 97.88 C +ATOM 365 N ALA A 46 15.960 4.948 11.536 1.00 97.88 N +ATOM 366 CA ALA A 46 14.789 5.033 10.661 1.00 97.88 C +ATOM 367 C ALA A 46 13.519 5.382 11.448 1.00 97.88 C +ATOM 368 CB ALA A 46 14.615 3.717 9.890 1.00 97.88 C +ATOM 369 O ALA A 46 12.772 6.266 11.038 1.00 97.88 O +ATOM 370 N GLU A 47 13.305 4.736 12.598 1.00 97.19 N +ATOM 371 CA GLU A 47 12.171 5.036 13.477 1.00 97.19 C +ATOM 372 C GLU A 47 12.268 6.427 14.104 1.00 97.19 C +ATOM 373 CB GLU A 47 12.040 3.982 14.590 1.00 97.19 C +ATOM 374 O GLU A 47 11.242 7.079 14.276 1.00 97.19 O +ATOM 375 CG GLU A 47 11.753 2.555 14.104 1.00 97.19 C +ATOM 376 CD GLU A 47 10.672 2.491 13.021 1.00 97.19 C +ATOM 377 OE1 GLU A 47 10.898 1.805 11.996 1.00 97.19 O +ATOM 378 OE2 GLU A 47 9.590 3.101 13.170 1.00 97.19 O +ATOM 379 N TYR A 48 13.482 6.896 14.408 1.00 96.31 N +ATOM 380 CA TYR A 48 13.709 8.244 14.931 1.00 96.31 C +ATOM 381 C TYR A 48 13.434 9.342 13.890 1.00 96.31 C +ATOM 382 CB TYR A 48 15.149 8.332 15.453 1.00 96.31 C +ATOM 383 O TYR A 48 12.873 10.384 14.222 1.00 96.31 O +ATOM 384 CG TYR A 48 15.499 9.690 16.027 1.00 96.31 C +ATOM 385 CD1 TYR A 48 16.164 10.646 15.233 1.00 96.31 C +ATOM 386 CD2 TYR A 48 15.112 10.011 17.341 1.00 96.31 C +ATOM 387 CE1 TYR A 48 16.440 11.924 15.755 1.00 96.31 C +ATOM 388 CE2 TYR A 48 15.386 11.288 17.867 1.00 96.31 C +ATOM 389 OH TYR A 48 16.310 13.484 17.569 1.00 96.31 O +ATOM 390 CZ TYR A 48 16.048 12.248 17.071 1.00 96.31 C +ATOM 391 N LEU A 49 13.838 9.127 12.634 1.00 96.44 N +ATOM 392 CA LEU A 49 13.672 10.106 11.553 1.00 96.44 C +ATOM 393 C LEU A 49 12.261 10.113 10.955 1.00 96.44 C +ATOM 394 CB LEU A 49 14.705 9.828 10.449 1.00 96.44 C +ATOM 395 O LEU A 49 11.853 11.116 10.366 1.00 96.44 O +ATOM 396 CG LEU A 49 16.165 10.088 10.851 1.00 96.44 C +ATOM 397 CD1 LEU A 49 17.076 9.675 9.696 1.00 96.44 C +ATOM 398 CD2 LEU A 49 16.443 11.558 11.173 1.00 96.44 C +ATOM 399 N ASP A 50 11.523 9.010 11.067 1.00 95.69 N +ATOM 400 CA ASP A 50 10.157 8.939 10.568 1.00 95.69 C +ATOM 401 C ASP A 50 9.202 9.714 11.481 1.00 95.69 C +ATOM 402 CB ASP A 50 9.721 7.485 10.382 1.00 95.69 C +ATOM 403 O ASP A 50 8.969 9.362 12.638 1.00 95.69 O +ATOM 404 CG ASP A 50 8.328 7.384 9.757 1.00 95.69 C +ATOM 405 OD1 ASP A 50 7.851 8.344 9.117 1.00 95.69 O +ATOM 406 OD2 ASP A 50 7.715 6.304 9.895 1.00 95.69 O +ATOM 407 N ARG A 51 8.602 10.774 10.938 1.00 96.19 N +ATOM 408 CA ARG A 51 7.618 11.574 11.660 1.00 96.19 C +ATOM 409 C ARG A 51 6.355 10.744 11.885 1.00 96.19 C +ATOM 410 CB ARG A 51 7.347 12.870 10.890 1.00 96.19 C +ATOM 411 O ARG A 51 5.556 10.567 10.967 1.00 96.19 O +ATOM 412 CG ARG A 51 6.376 13.773 11.661 1.00 96.19 C +ATOM 413 CD ARG A 51 6.117 15.057 10.875 1.00 96.19 C +ATOM 414 NE ARG A 51 5.069 15.868 11.518 1.00 96.19 N +ATOM 415 NH1 ARG A 51 5.197 17.682 10.117 1.00 96.19 N +ATOM 416 NH2 ARG A 51 3.685 17.659 11.759 1.00 96.19 N +ATOM 417 CZ ARG A 51 4.658 17.062 11.131 1.00 96.19 C +ATOM 418 N LYS A 52 6.146 10.293 13.119 1.00 97.00 N +ATOM 419 CA LYS A 52 4.953 9.538 13.520 1.00 97.00 C +ATOM 420 C LYS A 52 3.800 10.455 13.932 1.00 97.00 C +ATOM 421 CB LYS A 52 5.289 8.550 14.647 1.00 97.00 C +ATOM 422 O LYS A 52 4.009 11.580 14.384 1.00 97.00 O +ATOM 423 CG LYS A 52 6.527 7.668 14.421 1.00 97.00 C +ATOM 424 CD LYS A 52 6.516 6.826 13.137 1.00 97.00 C +ATOM 425 CE LYS A 52 7.710 5.868 13.233 1.00 97.00 C +ATOM 426 NZ LYS A 52 7.753 4.871 12.147 1.00 97.00 N +ATOM 427 N VAL A 53 2.581 9.939 13.826 1.00 97.38 N +ATOM 428 CA VAL A 53 1.355 10.533 14.376 1.00 97.38 C +ATOM 429 C VAL A 53 0.778 9.569 15.411 1.00 97.38 C +ATOM 430 CB VAL A 53 0.359 10.871 13.246 1.00 97.38 C +ATOM 431 O VAL A 53 0.826 8.350 15.230 1.00 97.38 O +ATOM 432 CG1 VAL A 53 -0.954 11.459 13.768 1.00 97.38 C +ATOM 433 CG2 VAL A 53 0.967 11.911 12.299 1.00 97.38 C +ATOM 434 N THR A 54 0.246 10.074 16.520 1.00 96.31 N +ATOM 435 CA THR A 54 -0.457 9.246 17.511 1.00 96.31 C +ATOM 436 C THR A 54 -1.887 8.969 17.057 1.00 96.31 C +ATOM 437 CB THR A 54 -0.459 9.900 18.898 1.00 96.31 C +ATOM 438 O THR A 54 -2.488 9.780 16.355 1.00 96.31 O +ATOM 439 CG2 THR A 54 0.964 10.041 19.439 1.00 96.31 C +ATOM 440 OG1 THR A 54 -1.018 11.189 18.827 1.00 96.31 O +ATOM 441 N ALA A 55 -2.451 7.834 17.466 1.00 95.50 N +ATOM 442 CA ALA A 55 -3.881 7.601 17.310 1.00 95.50 C +ATOM 443 C ALA A 55 -4.661 8.733 17.997 1.00 95.50 C +ATOM 444 CB ALA A 55 -4.225 6.237 17.901 1.00 95.50 C +ATOM 445 O ALA A 55 -4.227 9.237 19.037 1.00 95.50 O +ATOM 446 N ASN A 56 -5.774 9.157 17.402 1.00 92.06 N +ATOM 447 CA ASN A 56 -6.552 10.279 17.901 1.00 92.06 C +ATOM 448 C ASN A 56 -8.054 9.985 17.838 1.00 92.06 C +ATOM 449 CB ASN A 56 -6.153 11.557 17.140 1.00 92.06 C +ATOM 450 O ASN A 56 -8.658 9.970 16.764 1.00 92.06 O +ATOM 451 CG ASN A 56 -6.598 12.810 17.871 1.00 92.06 C +ATOM 452 ND2 ASN A 56 -6.236 13.970 17.379 1.00 92.06 N +ATOM 453 OD1 ASN A 56 -7.231 12.768 18.915 1.00 92.06 O +ATOM 454 N ALA A 57 -8.647 9.789 19.017 1.00 88.44 N +ATOM 455 CA ALA A 57 -10.092 9.684 19.189 1.00 88.44 C +ATOM 456 C ALA A 57 -10.795 11.051 19.125 1.00 88.44 C +ATOM 457 CB ALA A 57 -10.385 8.948 20.501 1.00 88.44 C +ATOM 458 O ALA A 57 -12.016 11.091 19.127 1.00 88.44 O +ATOM 459 N ASN A 58 -10.064 12.168 19.031 1.00 88.94 N +ATOM 460 CA ASN A 58 -10.635 13.434 18.577 1.00 88.94 C +ATOM 461 C ASN A 58 -10.563 13.487 17.044 1.00 88.94 C +ATOM 462 CB ASN A 58 -9.913 14.625 19.216 1.00 88.94 C +ATOM 463 O ASN A 58 -9.481 13.242 16.493 1.00 88.94 O +ATOM 464 CG ASN A 58 -10.029 14.596 20.722 1.00 88.94 C +ATOM 465 ND2 ASN A 58 -8.931 14.587 21.438 1.00 88.94 N +ATOM 466 OD1 ASN A 58 -11.107 14.570 21.275 1.00 88.94 O +ATOM 467 N PRO A 59 -11.664 13.808 16.339 1.00 89.50 N +ATOM 468 CA PRO A 59 -11.663 13.847 14.885 1.00 89.50 C +ATOM 469 C PRO A 59 -10.626 14.837 14.355 1.00 89.50 C +ATOM 470 CB PRO A 59 -13.077 14.256 14.457 1.00 89.50 C +ATOM 471 O PRO A 59 -10.630 16.010 14.722 1.00 89.50 O +ATOM 472 CG PRO A 59 -13.939 13.901 15.661 1.00 89.50 C +ATOM 473 CD PRO A 59 -12.993 14.134 16.838 1.00 89.50 C +ATOM 474 N VAL A 60 -9.775 14.378 13.442 1.00 91.44 N +ATOM 475 CA VAL A 60 -8.938 15.251 12.606 1.00 91.44 C +ATOM 476 C VAL A 60 -9.426 15.079 11.179 1.00 91.44 C +ATOM 477 CB VAL A 60 -7.437 14.949 12.745 1.00 91.44 C +ATOM 478 O VAL A 60 -9.480 13.947 10.692 1.00 91.44 O +ATOM 479 CG1 VAL A 60 -6.590 15.908 11.903 1.00 91.44 C +ATOM 480 CG2 VAL A 60 -6.973 15.035 14.204 1.00 91.44 C +ATOM 481 N ASP A 61 -9.841 16.182 10.557 1.00 87.81 N +ATOM 482 CA ASP A 61 -10.486 16.217 9.237 1.00 87.81 C +ATOM 483 C ASP A 61 -11.724 15.304 9.140 1.00 87.81 C +ATOM 484 CB ASP A 61 -9.445 15.997 8.127 1.00 87.81 C +ATOM 485 O ASP A 61 -11.965 14.644 8.132 1.00 87.81 O +ATOM 486 CG ASP A 61 -8.288 16.996 8.212 1.00 87.81 C +ATOM 487 OD1 ASP A 61 -8.553 18.170 8.559 1.00 87.81 O +ATOM 488 OD2 ASP A 61 -7.140 16.570 7.947 1.00 87.81 O +ATOM 489 N GLY A 62 -12.507 15.235 10.224 1.00 92.38 N +ATOM 490 CA GLY A 62 -13.709 14.396 10.300 1.00 92.38 C +ATOM 491 C GLY A 62 -13.427 12.896 10.440 1.00 92.38 C +ATOM 492 O GLY A 62 -14.308 12.085 10.160 1.00 92.38 O +ATOM 493 N VAL A 63 -12.216 12.509 10.858 1.00 95.50 N +ATOM 494 CA VAL A 63 -11.792 11.104 10.962 1.00 95.50 C +ATOM 495 C VAL A 63 -11.243 10.795 12.354 1.00 95.50 C +ATOM 496 CB VAL A 63 -10.765 10.777 9.857 1.00 95.50 C +ATOM 497 O VAL A 63 -10.321 11.462 12.833 1.00 95.50 O +ATOM 498 CG1 VAL A 63 -10.283 9.324 9.911 1.00 95.50 C +ATOM 499 CG2 VAL A 63 -11.355 11.028 8.466 1.00 95.50 C +ATOM 500 N PHE A 64 -11.763 9.745 12.988 1.00 95.31 N +ATOM 501 CA PHE A 64 -11.158 9.142 14.178 1.00 95.31 C +ATOM 502 C PHE A 64 -10.027 8.197 13.779 1.00 95.31 C +ATOM 503 CB PHE A 64 -12.203 8.344 14.964 1.00 95.31 C +ATOM 504 O PHE A 64 -10.112 7.543 12.737 1.00 95.31 O +ATOM 505 CG PHE A 64 -13.352 9.147 15.530 1.00 95.31 C +ATOM 506 CD1 PHE A 64 -13.098 10.171 16.456 1.00 95.31 C +ATOM 507 CD2 PHE A 64 -14.680 8.810 15.215 1.00 95.31 C +ATOM 508 CE1 PHE A 64 -14.170 10.835 17.077 1.00 95.31 C +ATOM 509 CE2 PHE A 64 -15.746 9.487 15.834 1.00 95.31 C +ATOM 510 CZ PHE A 64 -15.493 10.518 16.749 1.00 95.31 C +ATOM 511 N SER A 65 -9.009 8.057 14.629 1.00 96.56 N +ATOM 512 CA SER A 65 -8.051 6.958 14.519 1.00 96.56 C +ATOM 513 C SER A 65 -7.750 6.282 15.853 1.00 96.56 C +ATOM 514 CB SER A 65 -6.773 7.379 13.790 1.00 96.56 C +ATOM 515 O SER A 65 -7.631 6.938 16.885 1.00 96.56 O +ATOM 516 OG SER A 65 -6.019 8.344 14.508 1.00 96.56 O +ATOM 517 N PHE A 66 -7.605 4.959 15.826 1.00 96.50 N +ATOM 518 CA PHE A 66 -7.262 4.132 16.985 1.00 96.50 C +ATOM 519 C PHE A 66 -6.407 2.939 16.560 1.00 96.50 C +ATOM 520 CB PHE A 66 -8.520 3.711 17.753 1.00 96.50 C +ATOM 521 O PHE A 66 -6.448 2.518 15.405 1.00 96.50 O +ATOM 522 CG PHE A 66 -9.472 2.804 16.999 1.00 96.50 C +ATOM 523 CD1 PHE A 66 -10.339 3.338 16.027 1.00 96.50 C +ATOM 524 CD2 PHE A 66 -9.521 1.430 17.300 1.00 96.50 C +ATOM 525 CE1 PHE A 66 -11.264 2.506 15.374 1.00 96.50 C +ATOM 526 CE2 PHE A 66 -10.455 0.602 16.656 1.00 96.50 C +ATOM 527 CZ PHE A 66 -11.332 1.141 15.700 1.00 96.50 C +ATOM 528 N ASP A 67 -5.604 2.415 17.483 1.00 97.06 N +ATOM 529 CA ASP A 67 -4.716 1.287 17.212 1.00 97.06 C +ATOM 530 C ASP A 67 -5.321 -0.016 17.740 1.00 97.06 C +ATOM 531 CB ASP A 67 -3.313 1.551 17.783 1.00 97.06 C +ATOM 532 O ASP A 67 -5.845 -0.068 18.853 1.00 97.06 O +ATOM 533 CG ASP A 67 -2.591 2.715 17.093 1.00 97.06 C +ATOM 534 OD1 ASP A 67 -2.793 2.936 15.881 1.00 97.06 O +ATOM 535 OD2 ASP A 67 -1.810 3.447 17.741 1.00 97.06 O +ATOM 536 N VAL A 68 -5.212 -1.079 16.946 1.00 97.00 N +ATOM 537 CA VAL A 68 -5.657 -2.433 17.291 1.00 97.00 C +ATOM 538 C VAL A 68 -4.495 -3.408 17.170 1.00 97.00 C +ATOM 539 CB VAL A 68 -6.859 -2.895 16.444 1.00 97.00 C +ATOM 540 O VAL A 68 -3.670 -3.315 16.259 1.00 97.00 O +ATOM 541 CG1 VAL A 68 -8.085 -2.013 16.697 1.00 97.00 C +ATOM 542 CG2 VAL A 68 -6.561 -2.916 14.939 1.00 97.00 C +ATOM 543 N LEU A 69 -4.429 -4.369 18.089 1.00 97.62 N +ATOM 544 CA LEU A 69 -3.474 -5.471 18.018 1.00 97.62 C +ATOM 545 C LEU A 69 -4.151 -6.663 17.337 1.00 97.62 C +ATOM 546 CB LEU A 69 -2.948 -5.759 19.431 1.00 97.62 C +ATOM 547 O LEU A 69 -4.929 -7.379 17.966 1.00 97.62 O +ATOM 548 CG LEU A 69 -1.836 -6.819 19.494 1.00 97.62 C +ATOM 549 CD1 LEU A 69 -0.623 -6.468 18.637 1.00 97.62 C +ATOM 550 CD2 LEU A 69 -1.354 -6.914 20.942 1.00 97.62 C +ATOM 551 N ILE A 70 -3.864 -6.842 16.048 1.00 98.06 N +ATOM 552 CA ILE A 70 -4.466 -7.870 15.194 1.00 98.06 C +ATOM 553 C ILE A 70 -3.999 -9.269 15.606 1.00 98.06 C +ATOM 554 CB ILE A 70 -4.155 -7.559 13.709 1.00 98.06 C +ATOM 555 O ILE A 70 -4.820 -10.174 15.757 1.00 98.06 O +ATOM 556 CG1 ILE A 70 -4.961 -6.314 13.269 1.00 98.06 C +ATOM 557 CG2 ILE A 70 -4.418 -8.788 12.830 1.00 98.06 C +ATOM 558 CD1 ILE A 70 -4.951 -6.031 11.762 1.00 98.06 C +ATOM 559 N ASP A 71 -2.691 -9.445 15.814 1.00 98.25 N +ATOM 560 CA ASP A 71 -2.105 -10.719 16.235 1.00 98.25 C +ATOM 561 C ASP A 71 -1.022 -10.493 17.295 1.00 98.25 C +ATOM 562 CB ASP A 71 -1.579 -11.504 15.022 1.00 98.25 C +ATOM 563 O ASP A 71 -0.005 -9.837 17.058 1.00 98.25 O +ATOM 564 CG ASP A 71 -1.249 -12.963 15.371 1.00 98.25 C +ATOM 565 OD1 ASP A 71 -0.629 -13.222 16.429 1.00 98.25 O +ATOM 566 OD2 ASP A 71 -1.640 -13.878 14.619 1.00 98.25 O +ATOM 567 N ARG A 72 -1.244 -11.058 18.488 1.00 97.25 N +ATOM 568 CA ARG A 72 -0.334 -10.925 19.635 1.00 97.25 C +ATOM 569 C ARG A 72 0.963 -11.715 19.473 1.00 97.25 C +ATOM 570 CB ARG A 72 -1.032 -11.363 20.932 1.00 97.25 C +ATOM 571 O ARG A 72 1.959 -11.342 20.077 1.00 97.25 O +ATOM 572 CG ARG A 72 -2.229 -10.477 21.294 1.00 97.25 C +ATOM 573 CD ARG A 72 -2.725 -10.763 22.717 1.00 97.25 C +ATOM 574 NE ARG A 72 -3.781 -9.812 23.120 1.00 97.25 N +ATOM 575 NH1 ARG A 72 -2.457 -8.238 24.159 1.00 97.25 N +ATOM 576 NH2 ARG A 72 -4.662 -7.930 24.051 1.00 97.25 N +ATOM 577 CZ ARG A 72 -3.626 -8.671 23.774 1.00 97.25 C +ATOM 578 N ARG A 73 0.970 -12.798 18.691 1.00 97.56 N +ATOM 579 CA ARG A 73 2.130 -13.699 18.554 1.00 97.56 C +ATOM 580 C ARG A 73 3.274 -13.020 17.810 1.00 97.56 C +ATOM 581 CB ARG A 73 1.724 -14.973 17.801 1.00 97.56 C +ATOM 582 O ARG A 73 4.433 -13.244 18.133 1.00 97.56 O +ATOM 583 CG ARG A 73 0.569 -15.736 18.470 1.00 97.56 C +ATOM 584 CD ARG A 73 0.091 -16.885 17.580 1.00 97.56 C +ATOM 585 NE ARG A 73 -0.532 -16.390 16.336 1.00 97.56 N +ATOM 586 NH1 ARG A 73 -0.619 -18.398 15.223 1.00 97.56 N +ATOM 587 NH2 ARG A 73 -1.378 -16.545 14.241 1.00 97.56 N +ATOM 588 CZ ARG A 73 -0.840 -17.111 15.275 1.00 97.56 C +ATOM 589 N ILE A 74 2.922 -12.186 16.835 1.00 96.69 N +ATOM 590 CA ILE A 74 3.862 -11.445 15.983 1.00 96.69 C +ATOM 591 C ILE A 74 3.845 -9.933 16.247 1.00 96.69 C +ATOM 592 CB ILE A 74 3.624 -11.783 14.497 1.00 96.69 C +ATOM 593 O ILE A 74 4.509 -9.183 15.541 1.00 96.69 O +ATOM 594 CG1 ILE A 74 2.174 -11.473 14.072 1.00 96.69 C +ATOM 595 CG2 ILE A 74 3.969 -13.261 14.233 1.00 96.69 C +ATOM 596 CD1 ILE A 74 2.006 -11.392 12.553 1.00 96.69 C +ATOM 597 N ASN A 75 3.101 -9.480 17.263 1.00 96.69 N +ATOM 598 CA ASN A 75 2.888 -8.064 17.577 1.00 96.69 C +ATOM 599 C ASN A 75 2.394 -7.236 16.374 1.00 96.69 C +ATOM 600 CB ASN A 75 4.130 -7.476 18.267 1.00 96.69 C +ATOM 601 O ASN A 75 2.832 -6.102 16.168 1.00 96.69 O +ATOM 602 CG ASN A 75 4.414 -8.152 19.589 1.00 96.69 C +ATOM 603 ND2 ASN A 75 5.577 -8.739 19.748 1.00 96.69 N +ATOM 604 OD1 ASN A 75 3.592 -8.163 20.489 1.00 96.69 O +ATOM 605 N LEU A 76 1.475 -7.795 15.579 1.00 98.56 N +ATOM 606 CA LEU A 76 0.927 -7.111 14.410 1.00 98.56 C +ATOM 607 C LEU A 76 -0.073 -6.042 14.852 1.00 98.56 C +ATOM 608 CB LEU A 76 0.331 -8.124 13.423 1.00 98.56 C +ATOM 609 O LEU A 76 -1.255 -6.316 15.071 1.00 98.56 O +ATOM 610 CG LEU A 76 -0.197 -7.486 12.123 1.00 98.56 C +ATOM 611 CD1 LEU A 76 0.903 -6.769 11.338 1.00 98.56 C +ATOM 612 CD2 LEU A 76 -0.794 -8.577 11.240 1.00 98.56 C +ATOM 613 N LEU A 77 0.424 -4.819 15.007 1.00 97.94 N +ATOM 614 CA LEU A 77 -0.389 -3.630 15.227 1.00 97.94 C +ATOM 615 C LEU A 77 -1.000 -3.165 13.899 1.00 97.94 C +ATOM 616 CB LEU A 77 0.487 -2.540 15.872 1.00 97.94 C +ATOM 617 O LEU A 77 -0.430 -3.358 12.826 1.00 97.94 O +ATOM 618 CG LEU A 77 -0.301 -1.381 16.514 1.00 97.94 C +ATOM 619 CD1 LEU A 77 -0.926 -1.815 17.842 1.00 97.94 C +ATOM 620 CD2 LEU A 77 0.640 -0.208 16.787 1.00 97.94 C +ATOM 621 N SER A 78 -2.151 -2.516 13.941 1.00 98.50 N +ATOM 622 CA SER A 78 -2.691 -1.772 12.802 1.00 98.50 C +ATOM 623 C SER A 78 -3.425 -0.540 13.294 1.00 98.50 C +ATOM 624 CB SER A 78 -3.613 -2.659 11.968 1.00 98.50 C +ATOM 625 O SER A 78 -3.982 -0.548 14.392 1.00 98.50 O +ATOM 626 OG SER A 78 -2.830 -3.680 11.383 1.00 98.50 O +ATOM 627 N ARG A 79 -3.424 0.515 12.480 1.00 98.50 N +ATOM 628 CA ARG A 79 -4.207 1.714 12.762 1.00 98.50 C +ATOM 629 C ARG A 79 -5.501 1.682 11.977 1.00 98.50 C +ATOM 630 CB ARG A 79 -3.398 2.977 12.486 1.00 98.50 C +ATOM 631 O ARG A 79 -5.483 1.578 10.755 1.00 98.50 O +ATOM 632 CG ARG A 79 -4.261 4.202 12.833 1.00 98.50 C +ATOM 633 CD ARG A 79 -3.377 5.400 13.127 1.00 98.50 C +ATOM 634 NE ARG A 79 -2.757 5.231 14.441 1.00 98.50 N +ATOM 635 NH1 ARG A 79 -1.328 6.969 14.274 1.00 98.50 N +ATOM 636 NH2 ARG A 79 -1.140 5.526 15.988 1.00 98.50 N +ATOM 637 CZ ARG A 79 -1.747 5.911 14.904 1.00 98.50 C +ATOM 638 N VAL A 80 -6.613 1.819 12.679 1.00 98.62 N +ATOM 639 CA VAL A 80 -7.933 1.976 12.082 1.00 98.62 C +ATOM 640 C VAL A 80 -8.247 3.461 11.969 1.00 98.62 C +ATOM 641 CB VAL A 80 -8.999 1.244 12.907 1.00 98.62 C +ATOM 642 O VAL A 80 -8.073 4.208 12.930 1.00 98.62 O +ATOM 643 CG1 VAL A 80 -10.370 1.355 12.231 1.00 98.62 C +ATOM 644 CG2 VAL A 80 -8.657 -0.243 13.078 1.00 98.62 C +ATOM 645 N TYR A 81 -8.741 3.870 10.807 1.00 98.12 N +ATOM 646 CA TYR A 81 -9.306 5.182 10.529 1.00 98.12 C +ATOM 647 C TYR A 81 -10.771 5.015 10.149 1.00 98.12 C +ATOM 648 CB TYR A 81 -8.560 5.856 9.379 1.00 98.12 C +ATOM 649 O TYR A 81 -11.097 4.171 9.316 1.00 98.12 O +ATOM 650 CG TYR A 81 -7.087 6.058 9.609 1.00 98.12 C +ATOM 651 CD1 TYR A 81 -6.646 7.297 10.097 1.00 98.12 C +ATOM 652 CD2 TYR A 81 -6.166 5.028 9.331 1.00 98.12 C +ATOM 653 CE1 TYR A 81 -5.281 7.510 10.323 1.00 98.12 C +ATOM 654 CE2 TYR A 81 -4.792 5.244 9.550 1.00 98.12 C +ATOM 655 OH TYR A 81 -3.045 6.690 10.348 1.00 98.12 O +ATOM 656 CZ TYR A 81 -4.353 6.483 10.060 1.00 98.12 C +ATOM 657 N ARG A 82 -11.660 5.822 10.723 1.00 96.50 N +ATOM 658 CA ARG A 82 -13.094 5.762 10.408 1.00 96.50 C +ATOM 659 C ARG A 82 -13.751 7.141 10.453 1.00 96.50 C +ATOM 660 CB ARG A 82 -13.802 4.762 11.337 1.00 96.50 C +ATOM 661 O ARG A 82 -13.245 8.010 11.171 1.00 96.50 O +ATOM 662 CG ARG A 82 -13.649 5.098 12.831 1.00 96.50 C +ATOM 663 CD ARG A 82 -14.616 4.277 13.687 1.00 96.50 C +ATOM 664 NE ARG A 82 -15.992 4.775 13.527 1.00 96.50 N +ATOM 665 NH1 ARG A 82 -17.079 3.017 14.522 1.00 96.50 N +ATOM 666 NH2 ARG A 82 -18.238 4.648 13.554 1.00 96.50 N +ATOM 667 CZ ARG A 82 -17.091 4.141 13.876 1.00 96.50 C +ATOM 668 N PRO A 83 -14.872 7.349 9.740 1.00 94.81 N +ATOM 669 CA PRO A 83 -15.596 8.611 9.784 1.00 94.81 C +ATOM 670 C PRO A 83 -16.023 8.949 11.213 1.00 94.81 C +ATOM 671 CB PRO A 83 -16.804 8.446 8.855 1.00 94.81 C +ATOM 672 O PRO A 83 -16.408 8.061 11.987 1.00 94.81 O +ATOM 673 CG PRO A 83 -16.394 7.311 7.919 1.00 94.81 C +ATOM 674 CD PRO A 83 -15.544 6.430 8.829 1.00 94.81 C +ATOM 675 N ALA A 84 -15.943 10.232 11.551 1.00 90.50 N +ATOM 676 CA ALA A 84 -16.513 10.762 12.773 1.00 90.50 C +ATOM 677 C ALA A 84 -18.030 10.942 12.649 1.00 90.50 C +ATOM 678 CB ALA A 84 -15.787 12.049 13.170 1.00 90.50 C +ATOM 679 O ALA A 84 -18.549 11.218 11.568 1.00 90.50 O +ATOM 680 N TYR A 85 -18.731 10.789 13.771 1.00 79.38 N +ATOM 681 CA TYR A 85 -20.151 11.120 13.874 1.00 79.38 C +ATOM 682 C TYR A 85 -20.314 12.642 14.000 1.00 79.38 C +ATOM 683 CB TYR A 85 -20.761 10.385 15.073 1.00 79.38 C +ATOM 684 O TYR A 85 -19.446 13.296 14.578 1.00 79.38 O +ATOM 685 CG TYR A 85 -20.504 8.886 15.116 1.00 79.38 C +ATOM 686 CD1 TYR A 85 -21.231 8.015 14.279 1.00 79.38 C +ATOM 687 CD2 TYR A 85 -19.582 8.358 16.043 1.00 79.38 C +ATOM 688 CE1 TYR A 85 -21.072 6.620 14.400 1.00 79.38 C +ATOM 689 CE2 TYR A 85 -19.420 6.965 16.168 1.00 79.38 C +ATOM 690 OH TYR A 85 -20.107 4.750 15.564 1.00 79.38 O +ATOM 691 CZ TYR A 85 -20.195 6.095 15.372 1.00 79.38 C +ATOM 692 N ALA A 86 -21.409 13.202 13.479 1.00 67.88 N +ATOM 693 CA ALA A 86 -21.630 14.652 13.468 1.00 67.88 C +ATOM 694 C ALA A 86 -21.754 15.269 14.880 1.00 67.88 C +ATOM 695 CB ALA A 86 -22.869 14.942 12.609 1.00 67.88 C +ATOM 696 O ALA A 86 -21.281 16.382 15.092 1.00 67.88 O +ATOM 697 N ASP A 87 -22.304 14.523 15.848 1.00 65.94 N +ATOM 698 CA ASP A 87 -22.799 15.074 17.121 1.00 65.94 C +ATOM 699 C ASP A 87 -22.154 14.423 18.364 1.00 65.94 C +ATOM 700 CB ASP A 87 -24.334 14.941 17.158 1.00 65.94 C +ATOM 701 O ASP A 87 -22.830 13.780 19.168 1.00 65.94 O +ATOM 702 CG ASP A 87 -25.046 15.637 15.997 1.00 65.94 C +ATOM 703 OD1 ASP A 87 -24.635 16.761 15.636 1.00 65.94 O +ATOM 704 OD2 ASP A 87 -25.995 15.020 15.463 1.00 65.94 O +ATOM 705 N GLN A 88 -20.834 14.531 18.540 1.00 66.69 N +ATOM 706 CA GLN A 88 -20.154 14.002 19.737 1.00 66.69 C +ATOM 707 C GLN A 88 -19.533 15.131 20.571 1.00 66.69 C +ATOM 708 CB GLN A 88 -19.119 12.936 19.339 1.00 66.69 C +ATOM 709 O GLN A 88 -18.558 15.751 20.155 1.00 66.69 O +ATOM 710 CG GLN A 88 -19.741 11.635 18.799 1.00 66.69 C +ATOM 711 CD GLN A 88 -20.251 10.686 19.883 1.00 66.69 C +ATOM 712 NE2 GLN A 88 -21.500 10.284 19.840 1.00 66.69 N +ATOM 713 OE1 GLN A 88 -19.530 10.243 20.758 1.00 66.69 O +ATOM 714 N GLU A 89 -20.075 15.370 21.772 1.00 69.81 N +ATOM 715 CA GLU A 89 -19.503 16.312 22.753 1.00 69.81 C +ATOM 716 C GLU A 89 -18.233 15.768 23.428 1.00 69.81 C +ATOM 717 CB GLU A 89 -20.524 16.620 23.860 1.00 69.81 C +ATOM 718 O GLU A 89 -17.376 16.540 23.859 1.00 69.81 O +ATOM 719 CG GLU A 89 -21.737 17.438 23.393 1.00 69.81 C +ATOM 720 CD GLU A 89 -22.655 17.854 24.559 1.00 69.81 C +ATOM 721 OE1 GLU A 89 -23.558 18.684 24.310 1.00 69.81 O +ATOM 722 OE2 GLU A 89 -22.454 17.354 25.691 1.00 69.81 O +ATOM 723 N GLN A 90 -18.113 14.440 23.536 1.00 77.12 N +ATOM 724 CA GLN A 90 -16.991 13.758 24.182 1.00 77.12 C +ATOM 725 C GLN A 90 -16.312 12.769 23.225 1.00 77.12 C +ATOM 726 CB GLN A 90 -17.447 13.047 25.465 1.00 77.12 C +ATOM 727 O GLN A 90 -16.977 12.215 22.347 1.00 77.12 O +ATOM 728 CG GLN A 90 -17.899 14.047 26.539 1.00 77.12 C +ATOM 729 CD GLN A 90 -18.208 13.398 27.884 1.00 77.12 C +ATOM 730 NE2 GLN A 90 -18.600 14.179 28.865 1.00 77.12 N +ATOM 731 OE1 GLN A 90 -18.099 12.202 28.101 1.00 77.12 O +ATOM 732 N PRO A 91 -15.000 12.509 23.389 1.00 79.38 N +ATOM 733 CA PRO A 91 -14.299 11.531 22.568 1.00 79.38 C +ATOM 734 C PRO A 91 -14.860 10.120 22.819 1.00 79.38 C +ATOM 735 CB PRO A 91 -12.816 11.621 22.947 1.00 79.38 C +ATOM 736 O PRO A 91 -14.985 9.722 23.981 1.00 79.38 O +ATOM 737 CG PRO A 91 -12.708 12.925 23.737 1.00 79.38 C +ATOM 738 CD PRO A 91 -14.088 13.109 24.346 1.00 79.38 C +ATOM 739 N PRO A 92 -15.161 9.339 21.768 1.00 86.50 N +ATOM 740 CA PRO A 92 -15.657 7.976 21.919 1.00 86.50 C +ATOM 741 C PRO A 92 -14.606 7.064 22.562 1.00 86.50 C +ATOM 742 CB PRO A 92 -16.024 7.524 20.502 1.00 86.50 C +ATOM 743 O PRO A 92 -13.401 7.204 22.326 1.00 86.50 O +ATOM 744 CG PRO A 92 -15.098 8.352 19.620 1.00 86.50 C +ATOM 745 CD PRO A 92 -15.021 9.679 20.361 1.00 86.50 C +ATOM 746 N SER A 93 -15.058 6.075 23.336 1.00 87.94 N +ATOM 747 CA SER A 93 -14.176 5.000 23.794 1.00 87.94 C +ATOM 748 C SER A 93 -13.784 4.078 22.630 1.00 87.94 C +ATOM 749 CB SER A 93 -14.836 4.219 24.935 1.00 87.94 C +ATOM 750 O SER A 93 -14.428 4.067 21.581 1.00 87.94 O +ATOM 751 OG SER A 93 -15.861 3.379 24.451 1.00 87.94 O +ATOM 752 N ILE A 94 -12.762 3.232 22.811 1.00 85.69 N +ATOM 753 CA ILE A 94 -12.418 2.202 21.810 1.00 85.69 C +ATOM 754 C ILE A 94 -13.621 1.287 21.527 1.00 85.69 C +ATOM 755 CB ILE A 94 -11.171 1.399 22.250 1.00 85.69 C +ATOM 756 O ILE A 94 -13.867 0.949 20.375 1.00 85.69 O +ATOM 757 CG1 ILE A 94 -9.924 2.310 22.164 1.00 85.69 C +ATOM 758 CG2 ILE A 94 -10.970 0.132 21.393 1.00 85.69 C +ATOM 759 CD1 ILE A 94 -8.657 1.706 22.784 1.00 85.69 C +ATOM 760 N LEU A 95 -14.419 0.952 22.547 1.00 87.00 N +ATOM 761 CA LEU A 95 -15.631 0.143 22.371 1.00 87.00 C +ATOM 762 C LEU A 95 -16.691 0.860 21.526 1.00 87.00 C +ATOM 763 CB LEU A 95 -16.218 -0.214 23.748 1.00 87.00 C +ATOM 764 O LEU A 95 -17.440 0.207 20.805 1.00 87.00 O +ATOM 765 CG LEU A 95 -15.346 -1.148 24.603 1.00 87.00 C +ATOM 766 CD1 LEU A 95 -15.969 -1.290 25.993 1.00 87.00 C +ATOM 767 CD2 LEU A 95 -15.223 -2.545 23.990 1.00 87.00 C +ATOM 768 N ASP A 96 -16.754 2.191 21.587 1.00 88.88 N +ATOM 769 CA ASP A 96 -17.636 2.986 20.729 1.00 88.88 C +ATOM 770 C ASP A 96 -17.106 3.073 19.293 1.00 88.88 C +ATOM 771 CB ASP A 96 -17.818 4.389 21.316 1.00 88.88 C +ATOM 772 O ASP A 96 -17.885 3.052 18.338 1.00 88.88 O +ATOM 773 CG ASP A 96 -18.447 4.356 22.701 1.00 88.88 C +ATOM 774 OD1 ASP A 96 -19.536 3.743 22.822 1.00 88.88 O +ATOM 775 OD2 ASP A 96 -17.805 4.893 23.632 1.00 88.88 O +ATOM 776 N LEU A 97 -15.781 3.126 19.129 1.00 90.06 N +ATOM 777 CA LEU A 97 -15.112 3.114 17.827 1.00 90.06 C +ATOM 778 C LEU A 97 -15.176 1.745 17.131 1.00 90.06 C +ATOM 779 CB LEU A 97 -13.655 3.576 17.998 1.00 90.06 C +ATOM 780 O LEU A 97 -15.109 1.699 15.903 1.00 90.06 O +ATOM 781 CG LEU A 97 -13.492 5.057 18.384 1.00 90.06 C +ATOM 782 CD1 LEU A 97 -12.022 5.365 18.671 1.00 90.06 C +ATOM 783 CD2 LEU A 97 -13.950 5.986 17.256 1.00 90.06 C +ATOM 784 N GLU A 98 -15.375 0.662 17.882 1.00 91.19 N +ATOM 785 CA GLU A 98 -15.646 -0.692 17.378 1.00 91.19 C +ATOM 786 C GLU A 98 -17.152 -0.998 17.245 1.00 91.19 C +ATOM 787 CB GLU A 98 -14.918 -1.746 18.231 1.00 91.19 C +ATOM 788 O GLU A 98 -17.562 -2.157 17.198 1.00 91.19 O +ATOM 789 CG GLU A 98 -13.388 -1.669 18.109 1.00 91.19 C +ATOM 790 CD GLU A 98 -12.676 -2.881 18.737 1.00 91.19 C +ATOM 791 OE1 GLU A 98 -11.476 -3.067 18.424 1.00 91.19 O +ATOM 792 OE2 GLU A 98 -13.318 -3.670 19.468 1.00 91.19 O +ATOM 793 N LYS A 99 -18.011 0.026 17.163 1.00 90.75 N +ATOM 794 CA LYS A 99 -19.402 -0.130 16.700 1.00 90.75 C +ATOM 795 C LYS A 99 -19.477 0.028 15.177 1.00 90.75 C +ATOM 796 CB LYS A 99 -20.328 0.870 17.404 1.00 90.75 C +ATOM 797 O LYS A 99 -18.703 0.821 14.632 1.00 90.75 O +ATOM 798 CG LYS A 99 -20.472 0.531 18.894 1.00 90.75 C +ATOM 799 CD LYS A 99 -21.215 1.635 19.652 1.00 90.75 C +ATOM 800 CE LYS A 99 -21.308 1.255 21.134 1.00 90.75 C +ATOM 801 NZ LYS A 99 -21.701 2.410 21.976 1.00 90.75 N +ATOM 802 N PRO A 100 -20.408 -0.658 14.485 1.00 89.44 N +ATOM 803 CA PRO A 100 -20.624 -0.451 13.056 1.00 89.44 C +ATOM 804 C PRO A 100 -20.770 1.030 12.704 1.00 89.44 C +ATOM 805 CB PRO A 100 -21.886 -1.240 12.694 1.00 89.44 C +ATOM 806 O PRO A 100 -21.449 1.776 13.410 1.00 89.44 O +ATOM 807 CG PRO A 100 -21.956 -2.322 13.769 1.00 89.44 C +ATOM 808 CD PRO A 100 -21.328 -1.663 14.997 1.00 89.44 C +ATOM 809 N VAL A 101 -20.118 1.455 11.624 1.00 87.88 N +ATOM 810 CA VAL A 101 -20.333 2.789 11.051 1.00 87.88 C +ATOM 811 C VAL A 101 -21.761 2.856 10.501 1.00 87.88 C +ATOM 812 CB VAL A 101 -19.296 3.096 9.954 1.00 87.88 C +ATOM 813 O VAL A 101 -22.299 1.857 10.018 1.00 87.88 O +ATOM 814 CG1 VAL A 101 -19.471 4.486 9.328 1.00 87.88 C +ATOM 815 CG2 VAL A 101 -17.850 3.029 10.467 1.00 87.88 C +ATOM 816 N ASP A 102 -22.389 4.026 10.582 1.00 85.00 N +ATOM 817 CA ASP A 102 -23.706 4.223 9.989 1.00 85.00 C +ATOM 818 C ASP A 102 -23.628 4.283 8.461 1.00 85.00 C +ATOM 819 CB ASP A 102 -24.400 5.449 10.591 1.00 85.00 C +ATOM 820 O ASP A 102 -22.894 5.083 7.886 1.00 85.00 O +ATOM 821 CG ASP A 102 -24.880 5.172 12.017 1.00 85.00 C +ATOM 822 OD1 ASP A 102 -25.372 4.031 12.250 1.00 85.00 O +ATOM 823 OD2 ASP A 102 -24.762 6.095 12.847 1.00 85.00 O +ATOM 824 N GLY A 103 -24.400 3.406 7.824 1.00 85.38 N +ATOM 825 CA GLY A 103 -24.471 3.207 6.382 1.00 85.38 C +ATOM 826 C GLY A 103 -24.984 1.798 6.080 1.00 85.38 C +ATOM 827 O GLY A 103 -24.753 0.872 6.865 1.00 85.38 O +ATOM 828 N ASP A 104 -25.709 1.634 4.973 1.00 89.00 N +ATOM 829 CA ASP A 104 -26.188 0.316 4.537 1.00 89.00 C +ATOM 830 C ASP A 104 -25.023 -0.550 4.052 1.00 89.00 C +ATOM 831 CB ASP A 104 -27.255 0.469 3.440 1.00 89.00 C +ATOM 832 O ASP A 104 -24.880 -1.693 4.495 1.00 89.00 O +ATOM 833 CG ASP A 104 -28.647 0.813 3.978 1.00 89.00 C +ATOM 834 OD1 ASP A 104 -28.871 0.658 5.201 1.00 89.00 O +ATOM 835 OD2 ASP A 104 -29.490 1.196 3.140 1.00 89.00 O +ATOM 836 N ILE A 105 -24.162 0.027 3.207 1.00 93.06 N +ATOM 837 CA ILE A 105 -22.852 -0.512 2.844 1.00 93.06 C +ATOM 838 C ILE A 105 -21.771 0.462 3.309 1.00 93.06 C +ATOM 839 CB ILE A 105 -22.751 -0.855 1.340 1.00 93.06 C +ATOM 840 O ILE A 105 -21.838 1.661 3.048 1.00 93.06 O +ATOM 841 CG1 ILE A 105 -23.703 -2.028 1.008 1.00 93.06 C +ATOM 842 CG2 ILE A 105 -21.299 -1.262 1.017 1.00 93.06 C +ATOM 843 CD1 ILE A 105 -23.874 -2.319 -0.491 1.00 93.06 C +ATOM 844 N VAL A 106 -20.775 -0.070 4.014 1.00 95.31 N +ATOM 845 CA VAL A 106 -19.579 0.655 4.442 1.00 95.31 C +ATOM 846 C VAL A 106 -18.362 -0.121 3.936 1.00 95.31 C +ATOM 847 CB VAL A 106 -19.532 0.850 5.967 1.00 95.31 C +ATOM 848 O VAL A 106 -18.054 -1.184 4.487 1.00 95.31 O +ATOM 849 CG1 VAL A 106 -18.283 1.663 6.351 1.00 95.31 C +ATOM 850 CG2 VAL A 106 -20.778 1.594 6.473 1.00 95.31 C +ATOM 851 N PRO A 107 -17.663 0.380 2.902 1.00 97.81 N +ATOM 852 CA PRO A 107 -16.454 -0.264 2.418 1.00 97.81 C +ATOM 853 C PRO A 107 -15.349 -0.250 3.478 1.00 97.81 C +ATOM 854 CB PRO A 107 -16.059 0.444 1.119 1.00 97.81 C +ATOM 855 O PRO A 107 -15.245 0.667 4.302 1.00 97.81 O +ATOM 856 CG PRO A 107 -16.914 1.710 1.055 1.00 97.81 C +ATOM 857 CD PRO A 107 -18.026 1.529 2.081 1.00 97.81 C +ATOM 858 N VAL A 108 -14.490 -1.266 3.431 1.00 98.69 N +ATOM 859 CA VAL A 108 -13.315 -1.396 4.294 1.00 98.69 C +ATOM 860 C VAL A 108 -12.072 -1.525 3.432 1.00 98.69 C +ATOM 861 CB VAL A 108 -13.438 -2.578 5.268 1.00 98.69 C +ATOM 862 O VAL A 108 -11.881 -2.530 2.755 1.00 98.69 O +ATOM 863 CG1 VAL A 108 -12.237 -2.601 6.226 1.00 98.69 C +ATOM 864 CG2 VAL A 108 -14.717 -2.465 6.103 1.00 98.69 C +ATOM 865 N ILE A 109 -11.202 -0.522 3.481 1.00 98.88 N +ATOM 866 CA ILE A 109 -9.926 -0.525 2.770 1.00 98.88 C +ATOM 867 C ILE A 109 -8.835 -1.057 3.704 1.00 98.88 C +ATOM 868 CB ILE A 109 -9.602 0.876 2.212 1.00 98.88 C +ATOM 869 O ILE A 109 -8.480 -0.408 4.687 1.00 98.88 O +ATOM 870 CG1 ILE A 109 -10.700 1.358 1.231 1.00 98.88 C +ATOM 871 CG2 ILE A 109 -8.222 0.862 1.530 1.00 98.88 C +ATOM 872 CD1 ILE A 109 -10.472 2.757 0.644 1.00 98.88 C +ATOM 873 N LEU A 110 -8.257 -2.213 3.386 1.00 98.94 N +ATOM 874 CA LEU A 110 -7.026 -2.693 4.009 1.00 98.94 C +ATOM 875 C LEU A 110 -5.831 -2.052 3.293 1.00 98.94 C +ATOM 876 CB LEU A 110 -7.001 -4.228 3.963 1.00 98.94 C +ATOM 877 O LEU A 110 -5.539 -2.377 2.141 1.00 98.94 O +ATOM 878 CG LEU A 110 -5.860 -4.860 4.777 1.00 98.94 C +ATOM 879 CD1 LEU A 110 -6.030 -4.598 6.272 1.00 98.94 C +ATOM 880 CD2 LEU A 110 -5.870 -6.375 4.582 1.00 98.94 C +ATOM 881 N PHE A 111 -5.176 -1.105 3.962 1.00 98.94 N +ATOM 882 CA PHE A 111 -4.113 -0.287 3.391 1.00 98.94 C +ATOM 883 C PHE A 111 -2.719 -0.797 3.779 1.00 98.94 C +ATOM 884 CB PHE A 111 -4.318 1.192 3.746 1.00 98.94 C +ATOM 885 O PHE A 111 -2.411 -0.991 4.958 1.00 98.94 O +ATOM 886 CG PHE A 111 -3.314 2.108 3.070 1.00 98.94 C +ATOM 887 CD1 PHE A 111 -2.145 2.502 3.747 1.00 98.94 C +ATOM 888 CD2 PHE A 111 -3.518 2.519 1.739 1.00 98.94 C +ATOM 889 CE1 PHE A 111 -1.177 3.281 3.087 1.00 98.94 C +ATOM 890 CE2 PHE A 111 -2.547 3.291 1.076 1.00 98.94 C +ATOM 891 CZ PHE A 111 -1.367 3.657 1.746 1.00 98.94 C +ATOM 892 N PHE A 112 -1.853 -0.939 2.779 1.00 98.94 N +ATOM 893 CA PHE A 112 -0.443 -1.279 2.921 1.00 98.94 C +ATOM 894 C PHE A 112 0.408 -0.094 2.490 1.00 98.94 C +ATOM 895 CB PHE A 112 -0.122 -2.514 2.080 1.00 98.94 C +ATOM 896 O PHE A 112 0.361 0.341 1.340 1.00 98.94 O +ATOM 897 CG PHE A 112 -0.956 -3.714 2.451 1.00 98.94 C +ATOM 898 CD1 PHE A 112 -0.645 -4.440 3.609 1.00 98.94 C +ATOM 899 CD2 PHE A 112 -2.077 -4.065 1.678 1.00 98.94 C +ATOM 900 CE1 PHE A 112 -1.448 -5.523 3.988 1.00 98.94 C +ATOM 901 CE2 PHE A 112 -2.871 -5.162 2.048 1.00 98.94 C +ATOM 902 CZ PHE A 112 -2.555 -5.896 3.205 1.00 98.94 C +ATOM 903 N HIS A 113 1.202 0.429 3.414 1.00 98.75 N +ATOM 904 CA HIS A 113 2.052 1.569 3.122 1.00 98.75 C +ATOM 905 C HIS A 113 3.231 1.205 2.203 1.00 98.75 C +ATOM 906 CB HIS A 113 2.476 2.206 4.445 1.00 98.75 C +ATOM 907 O HIS A 113 3.734 0.083 2.205 1.00 98.75 O +ATOM 908 CG HIS A 113 3.431 1.408 5.291 1.00 98.75 C +ATOM 909 CD2 HIS A 113 3.140 0.696 6.423 1.00 98.75 C +ATOM 910 ND1 HIS A 113 4.783 1.299 5.080 1.00 98.75 N +ATOM 911 CE1 HIS A 113 5.294 0.525 6.044 1.00 98.75 C +ATOM 912 NE2 HIS A 113 4.334 0.148 6.902 1.00 98.75 N +ATOM 913 N GLY A 114 3.706 2.166 1.419 1.00 98.12 N +ATOM 914 CA GLY A 114 4.965 2.087 0.692 1.00 98.12 C +ATOM 915 C GLY A 114 6.183 2.265 1.598 1.00 98.12 C +ATOM 916 O GLY A 114 6.090 2.354 2.821 1.00 98.12 O +ATOM 917 N GLY A 115 7.363 2.349 0.987 1.00 96.75 N +ATOM 918 CA GLY A 115 8.636 2.386 1.719 1.00 96.75 C +ATOM 919 C GLY A 115 9.620 1.300 1.297 1.00 96.75 C +ATOM 920 O GLY A 115 10.612 1.065 1.979 1.00 96.75 O +ATOM 921 N SER A 116 9.393 0.653 0.151 1.00 97.81 N +ATOM 922 CA SER A 116 10.312 -0.348 -0.419 1.00 97.81 C +ATOM 923 C SER A 116 10.556 -1.527 0.524 1.00 97.81 C +ATOM 924 CB SER A 116 11.624 0.309 -0.860 1.00 97.81 C +ATOM 925 O SER A 116 11.691 -1.967 0.681 1.00 97.81 O +ATOM 926 OG SER A 116 11.333 1.532 -1.512 1.00 97.81 O +ATOM 927 N PHE A 117 9.495 -1.964 1.211 1.00 98.62 N +ATOM 928 CA PHE A 117 9.490 -2.998 2.258 1.00 98.62 C +ATOM 929 C PHE A 117 10.313 -2.680 3.513 1.00 98.62 C +ATOM 930 CB PHE A 117 9.889 -4.357 1.667 1.00 98.62 C +ATOM 931 O PHE A 117 10.180 -3.392 4.504 1.00 98.62 O +ATOM 932 CG PHE A 117 9.108 -4.736 0.429 1.00 98.62 C +ATOM 933 CD1 PHE A 117 7.833 -5.315 0.560 1.00 98.62 C +ATOM 934 CD2 PHE A 117 9.654 -4.509 -0.849 1.00 98.62 C +ATOM 935 CE1 PHE A 117 7.111 -5.677 -0.589 1.00 98.62 C +ATOM 936 CE2 PHE A 117 8.924 -4.859 -1.996 1.00 98.62 C +ATOM 937 CZ PHE A 117 7.658 -5.449 -1.862 1.00 98.62 C +ATOM 938 N ALA A 118 11.125 -1.619 3.501 1.00 98.44 N +ATOM 939 CA ALA A 118 12.059 -1.293 4.569 1.00 98.44 C +ATOM 940 C ALA A 118 11.650 -0.067 5.390 1.00 98.44 C +ATOM 941 CB ALA A 118 13.450 -1.116 3.948 1.00 98.44 C +ATOM 942 O ALA A 118 11.921 -0.021 6.585 1.00 98.44 O +ATOM 943 N HIS A 119 10.994 0.921 4.786 1.00 97.88 N +ATOM 944 CA HIS A 119 10.690 2.211 5.413 1.00 97.88 C +ATOM 945 C HIS A 119 9.230 2.348 5.833 1.00 97.88 C +ATOM 946 CB HIS A 119 11.089 3.345 4.466 1.00 97.88 C +ATOM 947 O HIS A 119 8.384 1.558 5.420 1.00 97.88 O +ATOM 948 CG HIS A 119 12.543 3.310 4.113 1.00 97.88 C +ATOM 949 CD2 HIS A 119 13.064 2.964 2.905 1.00 97.88 C +ATOM 950 ND1 HIS A 119 13.576 3.662 4.979 1.00 97.88 N +ATOM 951 CE1 HIS A 119 14.699 3.549 4.262 1.00 97.88 C +ATOM 952 NE2 HIS A 119 14.421 3.120 3.024 1.00 97.88 N +ATOM 953 N SER A 120 8.968 3.399 6.618 1.00 97.62 N +ATOM 954 CA SER A 120 7.636 3.832 7.064 1.00 97.62 C +ATOM 955 C SER A 120 6.886 2.811 7.920 1.00 97.62 C +ATOM 956 CB SER A 120 6.811 4.278 5.858 1.00 97.62 C +ATOM 957 O SER A 120 7.391 1.729 8.196 1.00 97.62 O +ATOM 958 OG SER A 120 7.448 5.375 5.222 1.00 97.62 O +ATOM 959 N SER A 121 5.723 3.189 8.425 1.00 98.06 N +ATOM 960 CA SER A 121 4.859 2.390 9.290 1.00 98.06 C +ATOM 961 C SER A 121 3.409 2.843 9.126 1.00 98.06 C +ATOM 962 CB SER A 121 5.309 2.533 10.748 1.00 98.06 C +ATOM 963 O SER A 121 3.137 3.910 8.567 1.00 98.06 O +ATOM 964 OG SER A 121 5.372 3.887 11.145 1.00 98.06 O +ATOM 965 N ALA A 122 2.464 2.088 9.691 1.00 98.06 N +ATOM 966 CA ALA A 122 1.041 2.449 9.663 1.00 98.06 C +ATOM 967 C ALA A 122 0.731 3.829 10.281 1.00 98.06 C +ATOM 968 CB ALA A 122 0.256 1.365 10.401 1.00 98.06 C +ATOM 969 O ALA A 122 -0.303 4.421 9.983 1.00 98.06 O +ATOM 970 N ASN A 123 1.620 4.341 11.139 1.00 97.25 N +ATOM 971 CA ASN A 123 1.501 5.631 11.817 1.00 97.25 C +ATOM 972 C ASN A 123 2.480 6.712 11.313 1.00 97.25 C +ATOM 973 CB ASN A 123 1.591 5.412 13.341 1.00 97.25 C +ATOM 974 O ASN A 123 2.605 7.747 11.972 1.00 97.25 O +ATOM 975 CG ASN A 123 2.964 4.993 13.831 1.00 97.25 C +ATOM 976 ND2 ASN A 123 3.244 5.139 15.104 1.00 97.25 N +ATOM 977 OD1 ASN A 123 3.804 4.508 13.092 1.00 97.25 O +ATOM 978 N SER A 124 3.180 6.514 10.187 1.00 98.25 N +ATOM 979 CA SER A 124 3.944 7.602 9.553 1.00 98.25 C +ATOM 980 C SER A 124 2.996 8.722 9.114 1.00 98.25 C +ATOM 981 CB SER A 124 4.704 7.141 8.307 1.00 98.25 C +ATOM 982 O SER A 124 1.943 8.458 8.535 1.00 98.25 O +ATOM 983 OG SER A 124 5.527 6.024 8.537 1.00 98.25 O +ATOM 984 N ALA A 125 3.376 9.984 9.310 1.00 96.94 N +ATOM 985 CA ALA A 125 2.506 11.139 9.070 1.00 96.94 C +ATOM 986 C ALA A 125 1.977 11.243 7.628 1.00 96.94 C +ATOM 987 CB ALA A 125 3.281 12.407 9.454 1.00 96.94 C +ATOM 988 O ALA A 125 0.842 11.673 7.412 1.00 96.94 O +ATOM 989 N ILE A 126 2.777 10.827 6.640 1.00 95.75 N +ATOM 990 CA ILE A 126 2.346 10.796 5.234 1.00 95.75 C +ATOM 991 C ILE A 126 1.205 9.802 4.998 1.00 95.75 C +ATOM 992 CB ILE A 126 3.523 10.513 4.277 1.00 95.75 C +ATOM 993 O ILE A 126 0.256 10.123 4.286 1.00 95.75 O +ATOM 994 CG1 ILE A 126 4.337 9.245 4.641 1.00 95.75 C +ATOM 995 CG2 ILE A 126 4.417 11.761 4.216 1.00 95.75 C +ATOM 996 CD1 ILE A 126 5.270 8.773 3.521 1.00 95.75 C +ATOM 997 N TYR A 127 1.258 8.634 5.639 1.00 97.88 N +ATOM 998 CA TYR A 127 0.228 7.609 5.509 1.00 97.88 C +ATOM 999 C TYR A 127 -0.965 7.889 6.408 1.00 97.88 C +ATOM 1000 CB TYR A 127 0.821 6.216 5.738 1.00 97.88 C +ATOM 1001 O TYR A 127 -2.082 7.599 5.997 1.00 97.88 O +ATOM 1002 CG TYR A 127 1.814 5.842 4.659 1.00 97.88 C +ATOM 1003 CD1 TYR A 127 1.403 5.834 3.312 1.00 97.88 C +ATOM 1004 CD2 TYR A 127 3.154 5.564 4.987 1.00 97.88 C +ATOM 1005 CE1 TYR A 127 2.334 5.580 2.293 1.00 97.88 C +ATOM 1006 CE2 TYR A 127 4.080 5.304 3.961 1.00 97.88 C +ATOM 1007 OH TYR A 127 4.594 5.022 1.666 1.00 97.88 O +ATOM 1008 CZ TYR A 127 3.671 5.291 2.617 1.00 97.88 C +ATOM 1009 N ASP A 128 -0.770 8.547 7.554 1.00 97.94 N +ATOM 1010 CA ASP A 128 -1.879 9.075 8.353 1.00 97.94 C +ATOM 1011 C ASP A 128 -2.727 10.054 7.531 1.00 97.94 C +ATOM 1012 CB ASP A 128 -1.333 9.725 9.636 1.00 97.94 C +ATOM 1013 O ASP A 128 -3.943 9.895 7.425 1.00 97.94 O +ATOM 1014 CG ASP A 128 -2.446 10.041 10.639 1.00 97.94 C +ATOM 1015 OD1 ASP A 128 -2.793 9.130 11.428 1.00 97.94 O +ATOM 1016 OD2 ASP A 128 -2.958 11.180 10.650 1.00 97.94 O +ATOM 1017 N THR A 129 -2.068 10.988 6.840 1.00 96.62 N +ATOM 1018 CA THR A 129 -2.729 11.948 5.944 1.00 96.62 C +ATOM 1019 C THR A 129 -3.452 11.244 4.791 1.00 96.62 C +ATOM 1020 CB THR A 129 -1.709 12.946 5.375 1.00 96.62 C +ATOM 1021 O THR A 129 -4.594 11.583 4.475 1.00 96.62 O +ATOM 1022 CG2 THR A 129 -2.370 14.041 4.537 1.00 96.62 C +ATOM 1023 OG1 THR A 129 -1.002 13.596 6.408 1.00 96.62 O +ATOM 1024 N LEU A 130 -2.810 10.256 4.154 1.00 97.44 N +ATOM 1025 CA LEU A 130 -3.409 9.511 3.044 1.00 97.44 C +ATOM 1026 C LEU A 130 -4.611 8.668 3.498 1.00 97.44 C +ATOM 1027 CB LEU A 130 -2.325 8.661 2.357 1.00 97.44 C +ATOM 1028 O LEU A 130 -5.657 8.721 2.858 1.00 97.44 O +ATOM 1029 CG LEU A 130 -2.818 7.857 1.136 1.00 97.44 C +ATOM 1030 CD1 LEU A 130 -3.431 8.745 0.049 1.00 97.44 C +ATOM 1031 CD2 LEU A 130 -1.638 7.107 0.520 1.00 97.44 C +ATOM 1032 N CYS A 131 -4.510 7.949 4.617 1.00 98.50 N +ATOM 1033 CA CYS A 131 -5.602 7.125 5.137 1.00 98.50 C +ATOM 1034 C CYS A 131 -6.812 7.977 5.542 1.00 98.50 C +ATOM 1035 CB CYS A 131 -5.115 6.284 6.322 1.00 98.50 C +ATOM 1036 O CYS A 131 -7.937 7.644 5.176 1.00 98.50 O +ATOM 1037 SG CYS A 131 -3.900 5.048 5.785 1.00 98.50 S +ATOM 1038 N ARG A 132 -6.604 9.116 6.224 1.00 97.94 N +ATOM 1039 CA ARG A 132 -7.694 10.060 6.545 1.00 97.94 C +ATOM 1040 C ARG A 132 -8.393 10.571 5.292 1.00 97.94 C +ATOM 1041 CB ARG A 132 -7.152 11.258 7.334 1.00 97.94 C +ATOM 1042 O ARG A 132 -9.618 10.646 5.247 1.00 97.94 O +ATOM 1043 CG ARG A 132 -6.776 10.866 8.761 1.00 97.94 C +ATOM 1044 CD ARG A 132 -6.174 12.070 9.489 1.00 97.94 C +ATOM 1045 NE ARG A 132 -5.695 11.676 10.819 1.00 97.94 N +ATOM 1046 NH1 ARG A 132 -7.738 11.531 11.844 1.00 97.94 N +ATOM 1047 NH2 ARG A 132 -5.876 10.888 12.924 1.00 97.94 N +ATOM 1048 CZ ARG A 132 -6.443 11.372 11.859 1.00 97.94 C +ATOM 1049 N ARG A 133 -7.620 10.871 4.250 1.00 97.31 N +ATOM 1050 CA ARG A 133 -8.162 11.288 2.959 1.00 97.31 C +ATOM 1051 C ARG A 133 -9.008 10.195 2.309 1.00 97.31 C +ATOM 1052 CB ARG A 133 -6.997 11.736 2.086 1.00 97.31 C +ATOM 1053 O ARG A 133 -10.107 10.498 1.859 1.00 97.31 O +ATOM 1054 CG ARG A 133 -7.471 12.246 0.726 1.00 97.31 C +ATOM 1055 CD ARG A 133 -6.278 12.744 -0.077 1.00 97.31 C +ATOM 1056 NE ARG A 133 -5.614 13.873 0.615 1.00 97.31 N +ATOM 1057 NH1 ARG A 133 -3.417 13.169 0.463 1.00 97.31 N +ATOM 1058 NH2 ARG A 133 -3.908 15.065 1.516 1.00 97.31 N +ATOM 1059 CZ ARG A 133 -4.323 14.023 0.856 1.00 97.31 C +ATOM 1060 N LEU A 134 -8.544 8.946 2.302 1.00 98.31 N +ATOM 1061 CA LEU A 134 -9.309 7.810 1.772 1.00 98.31 C +ATOM 1062 C LEU A 134 -10.637 7.614 2.522 1.00 98.31 C +ATOM 1063 CB LEU A 134 -8.453 6.533 1.831 1.00 98.31 C +ATOM 1064 O LEU A 134 -11.673 7.442 1.880 1.00 98.31 O +ATOM 1065 CG LEU A 134 -7.263 6.489 0.857 1.00 98.31 C +ATOM 1066 CD1 LEU A 134 -6.450 5.218 1.107 1.00 98.31 C +ATOM 1067 CD2 LEU A 134 -7.705 6.502 -0.604 1.00 98.31 C +ATOM 1068 N VAL A 135 -10.633 7.736 3.856 1.00 97.88 N +ATOM 1069 CA VAL A 135 -11.867 7.745 4.664 1.00 97.88 C +ATOM 1070 C VAL A 135 -12.802 8.872 4.221 1.00 97.88 C +ATOM 1071 CB VAL A 135 -11.555 7.875 6.168 1.00 97.88 C +ATOM 1072 O VAL A 135 -13.983 8.634 3.971 1.00 97.88 O +ATOM 1073 CG1 VAL A 135 -12.815 8.096 7.010 1.00 97.88 C +ATOM 1074 CG2 VAL A 135 -10.874 6.617 6.706 1.00 97.88 C +ATOM 1075 N GLY A 136 -12.286 10.096 4.085 1.00 95.25 N +ATOM 1076 CA GLY A 136 -13.078 11.258 3.680 1.00 95.25 C +ATOM 1077 C GLY A 136 -13.705 11.122 2.287 1.00 95.25 C +ATOM 1078 O GLY A 136 -14.876 11.480 2.123 1.00 95.25 O +ATOM 1079 N LEU A 137 -12.942 10.587 1.325 1.00 95.62 N +ATOM 1080 CA LEU A 137 -13.310 10.431 -0.089 1.00 95.62 C +ATOM 1081 C LEU A 137 -14.260 9.261 -0.358 1.00 95.62 C +ATOM 1082 CB LEU A 137 -12.033 10.193 -0.915 1.00 95.62 C +ATOM 1083 O LEU A 137 -15.158 9.388 -1.186 1.00 95.62 O +ATOM 1084 CG LEU A 137 -11.120 11.412 -1.100 1.00 95.62 C +ATOM 1085 CD1 LEU A 137 -9.821 10.936 -1.741 1.00 95.62 C +ATOM 1086 CD2 LEU A 137 -11.744 12.476 -2.003 1.00 95.62 C +ATOM 1087 N CYS A 138 -14.047 8.127 0.310 1.00 96.56 N +ATOM 1088 CA CYS A 138 -14.793 6.890 0.046 1.00 96.56 C +ATOM 1089 C CYS A 138 -15.834 6.584 1.130 1.00 96.56 C +ATOM 1090 CB CYS A 138 -13.818 5.716 -0.131 1.00 96.56 C +ATOM 1091 O CYS A 138 -16.474 5.543 1.078 1.00 96.56 O +ATOM 1092 SG CYS A 138 -12.482 6.101 -1.296 1.00 96.56 S +ATOM 1093 N LYS A 139 -15.948 7.440 2.161 1.00 94.50 N +ATOM 1094 CA LYS A 139 -16.809 7.225 3.343 1.00 94.50 C +ATOM 1095 C LYS A 139 -16.643 5.822 3.946 1.00 94.50 C +ATOM 1096 CB LYS A 139 -18.273 7.580 3.015 1.00 94.50 C +ATOM 1097 O LYS A 139 -17.593 5.203 4.409 1.00 94.50 O +ATOM 1098 CG LYS A 139 -18.479 9.005 2.476 1.00 94.50 C +ATOM 1099 CD LYS A 139 -17.896 10.075 3.407 1.00 94.50 C +ATOM 1100 CE LYS A 139 -18.225 11.467 2.870 1.00 94.50 C +ATOM 1101 NZ LYS A 139 -17.300 12.471 3.440 1.00 94.50 N +ATOM 1102 N CYS A 140 -15.403 5.345 3.940 1.00 96.81 N +ATOM 1103 CA CYS A 140 -15.024 3.979 4.275 1.00 96.81 C +ATOM 1104 C CYS A 140 -14.339 3.899 5.645 1.00 96.81 C +ATOM 1105 CB CYS A 140 -14.127 3.449 3.144 1.00 96.81 C +ATOM 1106 O CYS A 140 -13.959 4.918 6.227 1.00 96.81 O +ATOM 1107 SG CYS A 140 -12.503 4.262 3.135 1.00 96.81 S +ATOM 1108 N VAL A 141 -14.113 2.682 6.135 1.00 98.38 N +ATOM 1109 CA VAL A 141 -13.111 2.421 7.178 1.00 98.38 C +ATOM 1110 C VAL A 141 -11.791 2.060 6.501 1.00 98.38 C +ATOM 1111 CB VAL A 141 -13.574 1.310 8.139 1.00 98.38 C +ATOM 1112 O VAL A 141 -11.776 1.238 5.593 1.00 98.38 O +ATOM 1113 CG1 VAL A 141 -12.532 1.031 9.230 1.00 98.38 C +ATOM 1114 CG2 VAL A 141 -14.881 1.706 8.838 1.00 98.38 C +ATOM 1115 N VAL A 142 -10.672 2.627 6.950 1.00 98.81 N +ATOM 1116 CA VAL A 142 -9.331 2.230 6.490 1.00 98.81 C +ATOM 1117 C VAL A 142 -8.600 1.520 7.623 1.00 98.81 C +ATOM 1118 CB VAL A 142 -8.511 3.416 5.948 1.00 98.81 C +ATOM 1119 O VAL A 142 -8.462 2.071 8.713 1.00 98.81 O +ATOM 1120 CG1 VAL A 142 -7.125 2.972 5.466 1.00 98.81 C +ATOM 1121 CG2 VAL A 142 -9.198 4.131 4.780 1.00 98.81 C +ATOM 1122 N VAL A 143 -8.086 0.320 7.371 1.00 98.88 N +ATOM 1123 CA VAL A 143 -7.203 -0.419 8.281 1.00 98.88 C +ATOM 1124 C VAL A 143 -5.791 -0.382 7.705 1.00 98.88 C +ATOM 1125 CB VAL A 143 -7.708 -1.858 8.509 1.00 98.88 C +ATOM 1126 O VAL A 143 -5.490 -1.077 6.744 1.00 98.88 O +ATOM 1127 CG1 VAL A 143 -6.810 -2.602 9.508 1.00 98.88 C +ATOM 1128 CG2 VAL A 143 -9.141 -1.854 9.062 1.00 98.88 C +ATOM 1129 N SER A 144 -4.925 0.451 8.275 1.00 98.88 N +ATOM 1130 CA SER A 144 -3.525 0.602 7.869 1.00 98.88 C +ATOM 1131 C SER A 144 -2.650 -0.413 8.602 1.00 98.88 C +ATOM 1132 CB SER A 144 -3.075 2.037 8.139 1.00 98.88 C +ATOM 1133 O SER A 144 -2.513 -0.357 9.831 1.00 98.88 O +ATOM 1134 OG SER A 144 -1.771 2.270 7.648 1.00 98.88 O +ATOM 1135 N VAL A 145 -2.084 -1.364 7.858 1.00 98.88 N +ATOM 1136 CA VAL A 145 -1.358 -2.513 8.416 1.00 98.88 C +ATOM 1137 C VAL A 145 0.081 -2.136 8.747 1.00 98.88 C +ATOM 1138 CB VAL A 145 -1.403 -3.736 7.479 1.00 98.88 C +ATOM 1139 O VAL A 145 0.828 -1.689 7.877 1.00 98.88 O +ATOM 1140 CG1 VAL A 145 -0.805 -4.978 8.156 1.00 98.88 C +ATOM 1141 CG2 VAL A 145 -2.843 -4.076 7.093 1.00 98.88 C +ATOM 1142 N ASN A 146 0.505 -2.360 9.994 1.00 98.62 N +ATOM 1143 CA ASN A 146 1.890 -2.135 10.414 1.00 98.62 C +ATOM 1144 C ASN A 146 2.738 -3.398 10.211 1.00 98.62 C +ATOM 1145 CB ASN A 146 1.918 -1.613 11.857 1.00 98.62 C +ATOM 1146 O ASN A 146 3.230 -3.991 11.172 1.00 98.62 O +ATOM 1147 CG ASN A 146 3.236 -0.945 12.170 1.00 98.62 C +ATOM 1148 ND2 ASN A 146 3.798 -1.199 13.327 1.00 98.62 N +ATOM 1149 OD1 ASN A 146 3.754 -0.153 11.401 1.00 98.62 O +ATOM 1150 N TYR A 147 2.839 -3.838 8.958 1.00 98.81 N +ATOM 1151 CA TYR A 147 3.493 -5.091 8.593 1.00 98.81 C +ATOM 1152 C TYR A 147 4.994 -5.090 8.952 1.00 98.81 C +ATOM 1153 CB TYR A 147 3.225 -5.379 7.105 1.00 98.81 C +ATOM 1154 O TYR A 147 5.657 -4.048 9.007 1.00 98.81 O +ATOM 1155 CG TYR A 147 3.797 -4.351 6.144 1.00 98.81 C +ATOM 1156 CD1 TYR A 147 2.999 -3.318 5.607 1.00 98.81 C +ATOM 1157 CD2 TYR A 147 5.156 -4.432 5.799 1.00 98.81 C +ATOM 1158 CE1 TYR A 147 3.576 -2.369 4.737 1.00 98.81 C +ATOM 1159 CE2 TYR A 147 5.742 -3.473 4.961 1.00 98.81 C +ATOM 1160 OH TYR A 147 5.553 -1.524 3.628 1.00 98.81 O +ATOM 1161 CZ TYR A 147 4.952 -2.437 4.431 1.00 98.81 C +ATOM 1162 N ARG A 148 5.555 -6.277 9.197 1.00 98.69 N +ATOM 1163 CA ARG A 148 6.981 -6.479 9.471 1.00 98.69 C +ATOM 1164 C ARG A 148 7.822 -6.151 8.237 1.00 98.69 C +ATOM 1165 CB ARG A 148 7.216 -7.921 9.929 1.00 98.69 C +ATOM 1166 O ARG A 148 7.507 -6.560 7.123 1.00 98.69 O +ATOM 1167 CG ARG A 148 6.689 -8.204 11.345 1.00 98.69 C +ATOM 1168 CD ARG A 148 6.837 -9.684 11.715 1.00 98.69 C +ATOM 1169 NE ARG A 148 5.976 -10.547 10.888 1.00 98.69 N +ATOM 1170 NH1 ARG A 148 7.220 -12.476 10.986 1.00 98.69 N +ATOM 1171 NH2 ARG A 148 5.286 -12.436 9.863 1.00 98.69 N +ATOM 1172 CZ ARG A 148 6.165 -11.817 10.591 1.00 98.69 C +ATOM 1173 N ARG A 149 8.906 -5.404 8.451 1.00 98.31 N +ATOM 1174 CA ARG A 149 9.728 -4.800 7.392 1.00 98.31 C +ATOM 1175 C ARG A 149 11.067 -5.509 7.216 1.00 98.31 C +ATOM 1176 CB ARG A 149 9.896 -3.295 7.667 1.00 98.31 C +ATOM 1177 O ARG A 149 11.624 -6.056 8.169 1.00 98.31 O +ATOM 1178 CG ARG A 149 8.588 -2.529 7.388 1.00 98.31 C +ATOM 1179 CD ARG A 149 8.623 -1.085 7.892 1.00 98.31 C +ATOM 1180 NE ARG A 149 8.679 -1.046 9.364 1.00 98.31 N +ATOM 1181 NH1 ARG A 149 9.541 1.072 9.678 1.00 98.31 N +ATOM 1182 NH2 ARG A 149 9.349 -0.288 11.388 1.00 98.31 N +ATOM 1183 CZ ARG A 149 9.182 -0.093 10.119 1.00 98.31 C +ATOM 1184 N ALA A 150 11.574 -5.444 5.993 1.00 98.44 N +ATOM 1185 CA ALA A 150 12.913 -5.868 5.614 1.00 98.44 C +ATOM 1186 C ALA A 150 13.972 -4.830 6.045 1.00 98.44 C +ATOM 1187 CB ALA A 150 12.912 -6.083 4.097 1.00 98.44 C +ATOM 1188 O ALA A 150 13.656 -3.638 6.141 1.00 98.44 O +ATOM 1189 N PRO A 151 15.239 -5.236 6.244 1.00 97.56 N +ATOM 1190 CA PRO A 151 15.776 -6.596 6.099 1.00 97.56 C +ATOM 1191 C PRO A 151 15.535 -7.512 7.310 1.00 97.56 C +ATOM 1192 CB PRO A 151 17.276 -6.387 5.876 1.00 97.56 C +ATOM 1193 O PRO A 151 15.844 -8.693 7.239 1.00 97.56 O +ATOM 1194 CG PRO A 151 17.574 -5.141 6.708 1.00 97.56 C +ATOM 1195 CD PRO A 151 16.312 -4.299 6.534 1.00 97.56 C +ATOM 1196 N GLU A 152 15.001 -7.006 8.425 1.00 97.19 N +ATOM 1197 CA GLU A 152 14.849 -7.796 9.658 1.00 97.19 C +ATOM 1198 C GLU A 152 13.817 -8.920 9.512 1.00 97.19 C +ATOM 1199 CB GLU A 152 14.433 -6.891 10.829 1.00 97.19 C +ATOM 1200 O GLU A 152 13.915 -9.955 10.165 1.00 97.19 O +ATOM 1201 CG GLU A 152 15.389 -5.716 11.092 1.00 97.19 C +ATOM 1202 CD GLU A 152 15.086 -4.446 10.273 1.00 97.19 C +ATOM 1203 OE1 GLU A 152 15.608 -3.376 10.632 1.00 97.19 O +ATOM 1204 OE2 GLU A 152 14.277 -4.447 9.319 1.00 97.19 O +ATOM 1205 N ASN A 153 12.817 -8.695 8.663 1.00 98.00 N +ATOM 1206 CA ASN A 153 11.764 -9.644 8.339 1.00 98.00 C +ATOM 1207 C ASN A 153 11.593 -9.666 6.811 1.00 98.00 C +ATOM 1208 CB ASN A 153 10.480 -9.228 9.067 1.00 98.00 C +ATOM 1209 O ASN A 153 10.716 -8.970 6.287 1.00 98.00 O +ATOM 1210 CG ASN A 153 10.624 -9.164 10.572 1.00 98.00 C +ATOM 1211 ND2 ASN A 153 11.017 -8.024 11.093 1.00 98.00 N +ATOM 1212 OD1 ASN A 153 10.319 -10.096 11.297 1.00 98.00 O +ATOM 1213 N PRO A 154 12.459 -10.394 6.082 1.00 98.19 N +ATOM 1214 CA PRO A 154 12.363 -10.499 4.632 1.00 98.19 C +ATOM 1215 C PRO A 154 11.118 -11.309 4.231 1.00 98.19 C +ATOM 1216 CB PRO A 154 13.688 -11.148 4.204 1.00 98.19 C +ATOM 1217 O PRO A 154 10.278 -11.688 5.059 1.00 98.19 O +ATOM 1218 CG PRO A 154 14.019 -12.062 5.380 1.00 98.19 C +ATOM 1219 CD PRO A 154 13.521 -11.263 6.584 1.00 98.19 C +ATOM 1220 N TYR A 155 10.985 -11.571 2.937 1.00 98.69 N +ATOM 1221 CA TYR A 155 9.971 -12.450 2.375 1.00 98.69 C +ATOM 1222 C TYR A 155 9.854 -13.763 3.180 1.00 98.69 C +ATOM 1223 CB TYR A 155 10.340 -12.750 0.916 1.00 98.69 C +ATOM 1224 O TYR A 155 10.882 -14.359 3.513 1.00 98.69 O +ATOM 1225 CG TYR A 155 9.438 -13.769 0.247 1.00 98.69 C +ATOM 1226 CD1 TYR A 155 9.668 -15.145 0.444 1.00 98.69 C +ATOM 1227 CD2 TYR A 155 8.349 -13.349 -0.539 1.00 98.69 C +ATOM 1228 CE1 TYR A 155 8.842 -16.105 -0.163 1.00 98.69 C +ATOM 1229 CE2 TYR A 155 7.504 -14.312 -1.128 1.00 98.69 C +ATOM 1230 OH TYR A 155 6.942 -16.590 -1.548 1.00 98.69 O +ATOM 1231 CZ TYR A 155 7.754 -15.684 -0.950 1.00 98.69 C +ATOM 1232 N PRO A 156 8.629 -14.253 3.465 1.00 98.12 N +ATOM 1233 CA PRO A 156 7.318 -13.714 3.074 1.00 98.12 C +ATOM 1234 C PRO A 156 6.631 -12.858 4.162 1.00 98.12 C +ATOM 1235 CB PRO A 156 6.505 -14.961 2.715 1.00 98.12 C +ATOM 1236 O PRO A 156 5.421 -12.654 4.094 1.00 98.12 O +ATOM 1237 CG PRO A 156 6.989 -15.975 3.747 1.00 98.12 C +ATOM 1238 CD PRO A 156 8.462 -15.621 3.943 1.00 98.12 C +ATOM 1239 N CYS A 157 7.353 -12.341 5.167 1.00 98.81 N +ATOM 1240 CA CYS A 157 6.742 -11.808 6.397 1.00 98.81 C +ATOM 1241 C CYS A 157 5.675 -10.725 6.165 1.00 98.81 C +ATOM 1242 CB CYS A 157 7.833 -11.252 7.320 1.00 98.81 C +ATOM 1243 O CYS A 157 4.604 -10.791 6.761 1.00 98.81 O +ATOM 1244 SG CYS A 157 8.891 -12.587 7.943 1.00 98.81 S +ATOM 1245 N ALA A 158 5.931 -9.756 5.282 1.00 98.81 N +ATOM 1246 CA ALA A 158 4.963 -8.700 4.977 1.00 98.81 C +ATOM 1247 C ALA A 158 3.678 -9.241 4.315 1.00 98.81 C +ATOM 1248 CB ALA A 158 5.652 -7.658 4.090 1.00 98.81 C +ATOM 1249 O ALA A 158 2.590 -8.713 4.547 1.00 98.81 O +ATOM 1250 N TYR A 159 3.791 -10.309 3.518 1.00 98.81 N +ATOM 1251 CA TYR A 159 2.648 -10.967 2.879 1.00 98.81 C +ATOM 1252 C TYR A 159 1.837 -11.776 3.890 1.00 98.81 C +ATOM 1253 CB TYR A 159 3.129 -11.871 1.734 1.00 98.81 C +ATOM 1254 O TYR A 159 0.608 -11.724 3.871 1.00 98.81 O +ATOM 1255 CG TYR A 159 3.972 -11.217 0.652 1.00 98.81 C +ATOM 1256 CD1 TYR A 159 3.888 -9.832 0.386 1.00 98.81 C +ATOM 1257 CD2 TYR A 159 4.799 -12.030 -0.147 1.00 98.81 C +ATOM 1258 CE1 TYR A 159 4.625 -9.262 -0.669 1.00 98.81 C +ATOM 1259 CE2 TYR A 159 5.522 -11.458 -1.209 1.00 98.81 C +ATOM 1260 OH TYR A 159 6.119 -9.585 -2.527 1.00 98.81 O +ATOM 1261 CZ TYR A 159 5.438 -10.084 -1.471 1.00 98.81 C +ATOM 1262 N ASP A 160 2.509 -12.475 4.806 1.00 98.88 N +ATOM 1263 CA ASP A 160 1.843 -13.187 5.898 1.00 98.88 C +ATOM 1264 C ASP A 160 1.117 -12.230 6.844 1.00 98.88 C +ATOM 1265 CB ASP A 160 2.858 -14.028 6.678 1.00 98.88 C +ATOM 1266 O ASP A 160 -0.019 -12.494 7.235 1.00 98.88 O +ATOM 1267 CG ASP A 160 3.270 -15.298 5.936 1.00 98.88 C +ATOM 1268 OD1 ASP A 160 2.421 -15.851 5.186 1.00 98.88 O +ATOM 1269 OD2 ASP A 160 4.406 -15.746 6.203 1.00 98.88 O +ATOM 1270 N ASP A 161 1.723 -11.088 7.160 1.00 98.94 N +ATOM 1271 CA ASP A 161 1.098 -10.067 8.000 1.00 98.94 C +ATOM 1272 C ASP A 161 -0.133 -9.465 7.317 1.00 98.94 C +ATOM 1273 CB ASP A 161 2.116 -8.972 8.324 1.00 98.94 C +ATOM 1274 O ASP A 161 -1.177 -9.301 7.950 1.00 98.94 O +ATOM 1275 CG ASP A 161 3.246 -9.437 9.242 1.00 98.94 C +ATOM 1276 OD1 ASP A 161 3.295 -10.609 9.676 1.00 98.94 O +ATOM 1277 OD2 ASP A 161 4.113 -8.597 9.553 1.00 98.94 O +ATOM 1278 N GLY A 162 -0.052 -9.195 6.010 1.00 98.94 N +ATOM 1279 CA GLY A 162 -1.203 -8.744 5.235 1.00 98.94 C +ATOM 1280 C GLY A 162 -2.315 -9.786 5.147 1.00 98.94 C +ATOM 1281 O GLY A 162 -3.491 -9.441 5.266 1.00 98.94 O +ATOM 1282 N TRP A 163 -1.963 -11.067 5.037 1.00 98.81 N +ATOM 1283 CA TRP A 163 -2.927 -12.162 5.081 1.00 98.81 C +ATOM 1284 C TRP A 163 -3.626 -12.277 6.441 1.00 98.81 C +ATOM 1285 CB TRP A 163 -2.215 -13.470 4.737 1.00 98.81 C +ATOM 1286 O TRP A 163 -4.850 -12.418 6.508 1.00 98.81 O +ATOM 1287 CG TRP A 163 -3.119 -14.650 4.824 1.00 98.81 C +ATOM 1288 CD1 TRP A 163 -3.068 -15.635 5.748 1.00 98.81 C +ATOM 1289 CD2 TRP A 163 -4.306 -14.901 4.027 1.00 98.81 C +ATOM 1290 CE2 TRP A 163 -4.899 -16.114 4.487 1.00 98.81 C +ATOM 1291 CE3 TRP A 163 -4.951 -14.198 2.989 1.00 98.81 C +ATOM 1292 NE1 TRP A 163 -4.100 -16.526 5.529 1.00 98.81 N +ATOM 1293 CH2 TRP A 163 -6.748 -15.831 2.988 1.00 98.81 C +ATOM 1294 CZ2 TRP A 163 -6.107 -16.589 3.975 1.00 98.81 C +ATOM 1295 CZ3 TRP A 163 -6.170 -14.662 2.475 1.00 98.81 C +ATOM 1296 N ILE A 164 -2.866 -12.189 7.535 1.00 98.88 N +ATOM 1297 CA ILE A 164 -3.407 -12.180 8.899 1.00 98.88 C +ATOM 1298 C ILE A 164 -4.351 -10.984 9.080 1.00 98.88 C +ATOM 1299 CB ILE A 164 -2.246 -12.182 9.922 1.00 98.88 C +ATOM 1300 O ILE A 164 -5.455 -11.150 9.603 1.00 98.88 O +ATOM 1301 CG1 ILE A 164 -1.542 -13.558 9.933 1.00 98.88 C +ATOM 1302 CG2 ILE A 164 -2.740 -11.856 11.340 1.00 98.88 C +ATOM 1303 CD1 ILE A 164 -0.192 -13.555 10.662 1.00 98.88 C +ATOM 1304 N ALA A 165 -3.959 -9.800 8.603 1.00 98.94 N +ATOM 1305 CA ALA A 165 -4.780 -8.598 8.683 1.00 98.94 C +ATOM 1306 C ALA A 165 -6.079 -8.713 7.866 1.00 98.94 C +ATOM 1307 CB ALA A 165 -3.930 -7.395 8.270 1.00 98.94 C +ATOM 1308 O ALA A 165 -7.139 -8.339 8.368 1.00 98.94 O +ATOM 1309 N LEU A 166 -6.034 -9.304 6.666 1.00 98.88 N +ATOM 1310 CA LEU A 166 -7.227 -9.563 5.854 1.00 98.88 C +ATOM 1311 C LEU A 166 -8.216 -10.495 6.569 1.00 98.88 C +ATOM 1312 CB LEU A 166 -6.797 -10.130 4.487 1.00 98.88 C +ATOM 1313 O LEU A 166 -9.405 -10.189 6.658 1.00 98.88 O +ATOM 1314 CG LEU A 166 -7.986 -10.507 3.584 1.00 98.88 C +ATOM 1315 CD1 LEU A 166 -8.852 -9.298 3.246 1.00 98.88 C +ATOM 1316 CD2 LEU A 166 -7.501 -11.114 2.276 1.00 98.88 C +ATOM 1317 N ASN A 167 -7.730 -11.604 7.133 1.00 98.75 N +ATOM 1318 CA ASN A 167 -8.572 -12.523 7.905 1.00 98.75 C +ATOM 1319 C ASN A 167 -9.170 -11.838 9.136 1.00 98.75 C +ATOM 1320 CB ASN A 167 -7.754 -13.749 8.332 1.00 98.75 C +ATOM 1321 O ASN A 167 -10.346 -12.031 9.455 1.00 98.75 O +ATOM 1322 CG ASN A 167 -7.639 -14.738 7.201 1.00 98.75 C +ATOM 1323 ND2 ASN A 167 -6.642 -14.605 6.370 1.00 98.75 N +ATOM 1324 OD1 ASN A 167 -8.461 -15.620 7.042 1.00 98.75 O +ATOM 1325 N TRP A 168 -8.376 -11.012 9.820 1.00 98.62 N +ATOM 1326 CA TRP A 168 -8.858 -10.241 10.954 1.00 98.62 C +ATOM 1327 C TRP A 168 -9.986 -9.298 10.539 1.00 98.62 C +ATOM 1328 CB TRP A 168 -7.706 -9.504 11.622 1.00 98.62 C +ATOM 1329 O TRP A 168 -11.049 -9.374 11.155 1.00 98.62 O +ATOM 1330 CG TRP A 168 -8.106 -8.702 12.822 1.00 98.62 C +ATOM 1331 CD1 TRP A 168 -8.044 -9.115 14.108 1.00 98.62 C +ATOM 1332 CD2 TRP A 168 -8.592 -7.325 12.867 1.00 98.62 C +ATOM 1333 CE2 TRP A 168 -8.826 -6.975 14.229 1.00 98.62 C +ATOM 1334 CE3 TRP A 168 -8.821 -6.326 11.897 1.00 98.62 C +ATOM 1335 NE1 TRP A 168 -8.470 -8.100 14.942 1.00 98.62 N +ATOM 1336 CH2 TRP A 168 -9.522 -4.743 13.615 1.00 98.62 C +ATOM 1337 CZ2 TRP A 168 -9.284 -5.705 14.611 1.00 98.62 C +ATOM 1338 CZ3 TRP A 168 -9.289 -5.051 12.265 1.00 98.62 C +ATOM 1339 N VAL A 169 -9.805 -8.513 9.470 1.00 98.69 N +ATOM 1340 CA VAL A 169 -10.829 -7.606 8.924 1.00 98.69 C +ATOM 1341 C VAL A 169 -12.110 -8.366 8.589 1.00 98.69 C +ATOM 1342 CB VAL A 169 -10.291 -6.831 7.699 1.00 98.69 C +ATOM 1343 O VAL A 169 -13.170 -8.018 9.105 1.00 98.69 O +ATOM 1344 CG1 VAL A 169 -11.400 -6.209 6.843 1.00 98.69 C +ATOM 1345 CG2 VAL A 169 -9.375 -5.696 8.167 1.00 98.69 C +ATOM 1346 N ASN A 170 -12.009 -9.456 7.824 1.00 97.88 N +ATOM 1347 CA ASN A 170 -13.162 -10.266 7.421 1.00 97.88 C +ATOM 1348 C ASN A 170 -13.921 -10.878 8.622 1.00 97.88 C +ATOM 1349 CB ASN A 170 -12.648 -11.346 6.455 1.00 97.88 C +ATOM 1350 O ASN A 170 -15.120 -11.142 8.557 1.00 97.88 O +ATOM 1351 CG ASN A 170 -13.788 -12.073 5.765 1.00 97.88 C +ATOM 1352 ND2 ASN A 170 -13.717 -13.377 5.647 1.00 97.88 N +ATOM 1353 OD1 ASN A 170 -14.757 -11.486 5.326 1.00 97.88 O +ATOM 1354 N SER A 171 -13.232 -11.086 9.751 1.00 96.75 N +ATOM 1355 CA SER A 171 -13.822 -11.650 10.973 1.00 96.75 C +ATOM 1356 C SER A 171 -14.486 -10.626 11.907 1.00 96.75 C +ATOM 1357 CB SER A 171 -12.773 -12.461 11.741 1.00 96.75 C +ATOM 1358 O SER A 171 -15.203 -11.030 12.825 1.00 96.75 O +ATOM 1359 OG SER A 171 -11.840 -11.614 12.395 1.00 96.75 O +ATOM 1360 N ARG A 172 -14.250 -9.315 11.740 1.00 96.50 N +ATOM 1361 CA ARG A 172 -14.784 -8.304 12.669 1.00 96.50 C +ATOM 1362 C ARG A 172 -16.248 -8.006 12.361 1.00 96.50 C +ATOM 1363 CB ARG A 172 -13.972 -7.001 12.628 1.00 96.50 C +ATOM 1364 O ARG A 172 -16.576 -7.455 11.317 1.00 96.50 O +ATOM 1365 CG ARG A 172 -12.519 -7.044 13.123 1.00 96.50 C +ATOM 1366 CD ARG A 172 -12.323 -7.703 14.497 1.00 96.50 C +ATOM 1367 NE ARG A 172 -11.947 -9.126 14.398 1.00 96.50 N +ATOM 1368 NH1 ARG A 172 -11.945 -9.583 16.648 1.00 96.50 N +ATOM 1369 NH2 ARG A 172 -11.354 -11.171 15.191 1.00 96.50 N +ATOM 1370 CZ ARG A 172 -11.748 -9.950 15.410 1.00 96.50 C +ATOM 1371 N SER A 173 -17.136 -8.299 13.310 1.00 95.06 N +ATOM 1372 CA SER A 173 -18.574 -8.033 13.168 1.00 95.06 C +ATOM 1373 C SER A 173 -18.896 -6.557 12.940 1.00 95.06 C +ATOM 1374 CB SER A 173 -19.336 -8.524 14.402 1.00 95.06 C +ATOM 1375 O SER A 173 -19.803 -6.249 12.179 1.00 95.06 O +ATOM 1376 OG SER A 173 -18.741 -8.007 15.578 1.00 95.06 O +ATOM 1377 N TRP A 174 -18.141 -5.648 13.554 1.00 95.25 N +ATOM 1378 CA TRP A 174 -18.357 -4.208 13.427 1.00 95.25 C +ATOM 1379 C TRP A 174 -17.866 -3.607 12.105 1.00 95.25 C +ATOM 1380 CB TRP A 174 -17.723 -3.523 14.628 1.00 95.25 C +ATOM 1381 O TRP A 174 -18.206 -2.471 11.795 1.00 95.25 O +ATOM 1382 CG TRP A 174 -16.231 -3.615 14.709 1.00 95.25 C +ATOM 1383 CD1 TRP A 174 -15.524 -4.474 15.479 1.00 95.25 C +ATOM 1384 CD2 TRP A 174 -15.250 -2.741 14.079 1.00 95.25 C +ATOM 1385 CE2 TRP A 174 -13.950 -3.190 14.452 1.00 95.25 C +ATOM 1386 CE3 TRP A 174 -15.328 -1.585 13.272 1.00 95.25 C +ATOM 1387 NE1 TRP A 174 -14.173 -4.219 15.339 1.00 95.25 N +ATOM 1388 CH2 TRP A 174 -12.894 -1.459 13.145 1.00 95.25 C +ATOM 1389 CZ2 TRP A 174 -12.784 -2.570 13.993 1.00 95.25 C +ATOM 1390 CZ3 TRP A 174 -14.161 -0.943 12.819 1.00 95.25 C +ATOM 1391 N LEU A 175 -17.107 -4.368 11.311 1.00 96.94 N +ATOM 1392 CA LEU A 175 -16.729 -4.004 9.943 1.00 96.94 C +ATOM 1393 C LEU A 175 -17.744 -4.502 8.903 1.00 96.94 C +ATOM 1394 CB LEU A 175 -15.312 -4.539 9.659 1.00 96.94 C +ATOM 1395 O LEU A 175 -17.594 -4.224 7.716 1.00 96.94 O +ATOM 1396 CG LEU A 175 -14.194 -3.797 10.410 1.00 96.94 C +ATOM 1397 CD1 LEU A 175 -12.824 -4.407 10.107 1.00 96.94 C +ATOM 1398 CD2 LEU A 175 -14.110 -2.328 9.996 1.00 96.94 C +ATOM 1399 N LYS A 176 -18.771 -5.249 9.323 1.00 96.06 N +ATOM 1400 CA LYS A 176 -19.811 -5.740 8.421 1.00 96.06 C +ATOM 1401 C LYS A 176 -20.806 -4.636 8.083 1.00 96.06 C +ATOM 1402 CB LYS A 176 -20.529 -6.958 9.005 1.00 96.06 C +ATOM 1403 O LYS A 176 -21.280 -3.918 8.961 1.00 96.06 O +ATOM 1404 CG LYS A 176 -19.584 -8.157 9.128 1.00 96.06 C +ATOM 1405 CD LYS A 176 -20.298 -9.332 9.794 1.00 96.06 C +ATOM 1406 CE LYS A 176 -19.427 -10.580 9.649 1.00 96.06 C +ATOM 1407 NZ LYS A 176 -20.234 -11.803 9.851 1.00 96.06 N +ATOM 1408 N SER A 177 -21.163 -4.571 6.810 1.00 94.44 N +ATOM 1409 CA SER A 177 -22.222 -3.723 6.282 1.00 94.44 C +ATOM 1410 C SER A 177 -23.596 -4.217 6.731 1.00 94.44 C +ATOM 1411 CB SER A 177 -22.111 -3.709 4.762 1.00 94.44 C +ATOM 1412 O SER A 177 -23.847 -5.424 6.792 1.00 94.44 O +ATOM 1413 OG SER A 177 -20.975 -2.936 4.425 1.00 94.44 O +ATOM 1414 N LYS A 178 -24.502 -3.286 7.050 1.00 92.81 N +ATOM 1415 CA LYS A 178 -25.847 -3.601 7.562 1.00 92.81 C +ATOM 1416 C LYS A 178 -26.702 -4.319 6.512 1.00 92.81 C +ATOM 1417 CB LYS A 178 -26.542 -2.312 8.047 1.00 92.81 C +ATOM 1418 O LYS A 178 -27.458 -5.220 6.868 1.00 92.81 O +ATOM 1419 CG LYS A 178 -25.899 -1.706 9.311 1.00 92.81 C +ATOM 1420 CD LYS A 178 -26.580 -0.386 9.728 1.00 92.81 C +ATOM 1421 CE LYS A 178 -25.921 0.220 10.985 1.00 92.81 C +ATOM 1422 NZ LYS A 178 -26.415 1.595 11.294 1.00 92.81 N +ATOM 1423 N LYS A 179 -26.547 -3.955 5.234 1.00 93.25 N +ATOM 1424 CA LYS A 179 -27.355 -4.450 4.106 1.00 93.25 C +ATOM 1425 C LYS A 179 -27.322 -5.970 3.949 1.00 93.25 C +ATOM 1426 CB LYS A 179 -26.849 -3.760 2.832 1.00 93.25 C +ATOM 1427 O LYS A 179 -28.351 -6.572 3.660 1.00 93.25 O +ATOM 1428 CG LYS A 179 -27.692 -4.040 1.576 1.00 93.25 C +ATOM 1429 CD LYS A 179 -27.004 -3.353 0.395 1.00 93.25 C +ATOM 1430 CE LYS A 179 -27.702 -3.503 -0.958 1.00 93.25 C +ATOM 1431 NZ LYS A 179 -26.926 -2.786 -2.010 1.00 93.25 N +ATOM 1432 N ASP A 180 -26.155 -6.588 4.116 1.00 93.31 N +ATOM 1433 CA ASP A 180 -25.939 -8.007 3.804 1.00 93.31 C +ATOM 1434 C ASP A 180 -25.078 -8.759 4.831 1.00 93.31 C +ATOM 1435 CB ASP A 180 -25.356 -8.119 2.389 1.00 93.31 C +ATOM 1436 O ASP A 180 -24.799 -9.946 4.654 1.00 93.31 O +ATOM 1437 CG ASP A 180 -23.997 -7.433 2.221 1.00 93.31 C +ATOM 1438 OD1 ASP A 180 -23.391 -6.993 3.227 1.00 93.31 O +ATOM 1439 OD2 ASP A 180 -23.555 -7.346 1.060 1.00 93.31 O +ATOM 1440 N SER A 181 -24.680 -8.105 5.928 1.00 92.25 N +ATOM 1441 CA SER A 181 -23.829 -8.688 6.974 1.00 92.25 C +ATOM 1442 C SER A 181 -22.481 -9.223 6.460 1.00 92.25 C +ATOM 1443 CB SER A 181 -24.607 -9.730 7.786 1.00 92.25 C +ATOM 1444 O SER A 181 -21.899 -10.134 7.069 1.00 92.25 O +ATOM 1445 OG SER A 181 -25.729 -9.134 8.403 1.00 92.25 O +ATOM 1446 N LYS A 182 -21.961 -8.647 5.368 1.00 94.38 N +ATOM 1447 CA LYS A 182 -20.628 -8.924 4.815 1.00 94.38 C +ATOM 1448 C LYS A 182 -19.681 -7.752 5.036 1.00 94.38 C +ATOM 1449 CB LYS A 182 -20.730 -9.216 3.316 1.00 94.38 C +ATOM 1450 O LYS A 182 -20.110 -6.618 5.230 1.00 94.38 O +ATOM 1451 CG LYS A 182 -21.575 -10.454 2.993 1.00 94.38 C +ATOM 1452 CD LYS A 182 -21.569 -10.633 1.477 1.00 94.38 C +ATOM 1453 CE LYS A 182 -22.385 -11.841 1.028 1.00 94.38 C +ATOM 1454 NZ LYS A 182 -22.287 -11.954 -0.448 1.00 94.38 N +ATOM 1455 N VAL A 183 -18.380 -8.022 5.005 1.00 96.88 N +ATOM 1456 CA VAL A 183 -17.362 -6.967 4.961 1.00 96.88 C +ATOM 1457 C VAL A 183 -17.060 -6.684 3.491 1.00 96.88 C +ATOM 1458 CB VAL A 183 -16.095 -7.359 5.742 1.00 96.88 C +ATOM 1459 O VAL A 183 -16.666 -7.590 2.766 1.00 96.88 O +ATOM 1460 CG1 VAL A 183 -15.174 -6.145 5.877 1.00 96.88 C +ATOM 1461 CG2 VAL A 183 -16.421 -7.844 7.163 1.00 96.88 C +ATOM 1462 N HIS A 184 -17.265 -5.444 3.051 1.00 97.50 N +ATOM 1463 CA HIS A 184 -17.029 -5.011 1.669 1.00 97.50 C +ATOM 1464 C HIS A 184 -15.562 -4.590 1.531 1.00 97.50 C +ATOM 1465 CB HIS A 184 -18.038 -3.901 1.323 1.00 97.50 C +ATOM 1466 O HIS A 184 -15.218 -3.426 1.736 1.00 97.50 O +ATOM 1467 CG HIS A 184 -19.441 -4.418 1.101 1.00 97.50 C +ATOM 1468 CD2 HIS A 184 -20.266 -4.986 2.031 1.00 97.50 C +ATOM 1469 ND1 HIS A 184 -20.082 -4.452 -0.131 1.00 97.50 N +ATOM 1470 CE1 HIS A 184 -21.273 -5.027 0.075 1.00 97.50 C +ATOM 1471 NE2 HIS A 184 -21.414 -5.363 1.367 1.00 97.50 N +ATOM 1472 N ILE A 185 -14.676 -5.568 1.319 1.00 98.69 N +ATOM 1473 CA ILE A 185 -13.223 -5.388 1.456 1.00 98.69 C +ATOM 1474 C ILE A 185 -12.600 -4.888 0.153 1.00 98.69 C +ATOM 1475 CB ILE A 185 -12.519 -6.680 1.938 1.00 98.69 C +ATOM 1476 O ILE A 185 -12.758 -5.499 -0.897 1.00 98.69 O +ATOM 1477 CG1 ILE A 185 -13.102 -7.159 3.285 1.00 98.69 C +ATOM 1478 CG2 ILE A 185 -11.002 -6.435 2.087 1.00 98.69 C +ATOM 1479 CD1 ILE A 185 -12.651 -8.555 3.728 1.00 98.69 C +ATOM 1480 N PHE A 186 -11.784 -3.846 0.254 1.00 98.88 N +ATOM 1481 CA PHE A 186 -10.854 -3.409 -0.781 1.00 98.88 C +ATOM 1482 C PHE A 186 -9.427 -3.503 -0.247 1.00 98.88 C +ATOM 1483 CB PHE A 186 -11.190 -1.978 -1.202 1.00 98.88 C +ATOM 1484 O PHE A 186 -9.176 -3.215 0.924 1.00 98.88 O +ATOM 1485 CG PHE A 186 -12.481 -1.857 -1.983 1.00 98.88 C +ATOM 1486 CD1 PHE A 186 -12.440 -1.796 -3.388 1.00 98.88 C +ATOM 1487 CD2 PHE A 186 -13.720 -1.819 -1.313 1.00 98.88 C +ATOM 1488 CE1 PHE A 186 -13.631 -1.684 -4.123 1.00 98.88 C +ATOM 1489 CE2 PHE A 186 -14.912 -1.712 -2.050 1.00 98.88 C +ATOM 1490 CZ PHE A 186 -14.862 -1.636 -3.451 1.00 98.88 C +ATOM 1491 N LEU A 187 -8.469 -3.865 -1.096 1.00 98.94 N +ATOM 1492 CA LEU A 187 -7.051 -3.735 -0.769 1.00 98.94 C +ATOM 1493 C LEU A 187 -6.507 -2.476 -1.429 1.00 98.94 C +ATOM 1494 CB LEU A 187 -6.251 -4.972 -1.193 1.00 98.94 C +ATOM 1495 O LEU A 187 -6.810 -2.186 -2.584 1.00 98.94 O +ATOM 1496 CG LEU A 187 -6.820 -6.312 -0.700 1.00 98.94 C +ATOM 1497 CD1 LEU A 187 -5.913 -7.446 -1.156 1.00 98.94 C +ATOM 1498 CD2 LEU A 187 -6.923 -6.394 0.819 1.00 98.94 C +ATOM 1499 N ALA A 188 -5.667 -1.740 -0.717 1.00 98.94 N +ATOM 1500 CA ALA A 188 -4.984 -0.589 -1.277 1.00 98.94 C +ATOM 1501 C ALA A 188 -3.535 -0.547 -0.813 1.00 98.94 C +ATOM 1502 CB ALA A 188 -5.749 0.686 -0.914 1.00 98.94 C +ATOM 1503 O ALA A 188 -3.211 -0.942 0.304 1.00 98.94 O +ATOM 1504 N GLY A 189 -2.652 -0.030 -1.651 1.00 98.81 N +ATOM 1505 CA GLY A 189 -1.291 0.233 -1.221 1.00 98.81 C +ATOM 1506 C GLY A 189 -0.470 0.923 -2.283 1.00 98.81 C +ATOM 1507 O GLY A 189 -0.776 0.847 -3.476 1.00 98.81 O +ATOM 1508 N ASP A 190 0.585 1.595 -1.839 1.00 98.50 N +ATOM 1509 CA ASP A 190 1.476 2.324 -2.724 1.00 98.50 C +ATOM 1510 C ASP A 190 2.882 1.725 -2.754 1.00 98.50 C +ATOM 1511 CB ASP A 190 1.451 3.814 -2.390 1.00 98.50 C +ATOM 1512 O ASP A 190 3.376 1.215 -1.750 1.00 98.50 O +ATOM 1513 CG ASP A 190 2.353 4.163 -1.219 1.00 98.50 C +ATOM 1514 OD1 ASP A 190 2.012 3.735 -0.095 1.00 98.50 O +ATOM 1515 OD2 ASP A 190 3.359 4.866 -1.472 1.00 98.50 O +ATOM 1516 N SER A 191 3.551 1.750 -3.908 1.00 98.25 N +ATOM 1517 CA SER A 191 4.902 1.193 -4.044 1.00 98.25 C +ATOM 1518 C SER A 191 4.973 -0.263 -3.543 1.00 98.25 C +ATOM 1519 CB SER A 191 5.892 2.168 -3.390 1.00 98.25 C +ATOM 1520 O SER A 191 4.233 -1.122 -4.030 1.00 98.25 O +ATOM 1521 OG SER A 191 7.223 1.837 -3.740 1.00 98.25 O +ATOM 1522 N SER A 192 5.836 -0.578 -2.570 1.00 98.69 N +ATOM 1523 CA SER A 192 5.883 -1.906 -1.936 1.00 98.69 C +ATOM 1524 C SER A 192 4.555 -2.330 -1.306 1.00 98.69 C +ATOM 1525 CB SER A 192 6.973 -1.962 -0.867 1.00 98.69 C +ATOM 1526 O SER A 192 4.245 -3.514 -1.291 1.00 98.69 O +ATOM 1527 OG SER A 192 6.851 -0.909 0.084 1.00 98.69 O +ATOM 1528 N GLY A 193 3.744 -1.387 -0.831 1.00 98.88 N +ATOM 1529 CA GLY A 193 2.405 -1.662 -0.324 1.00 98.88 C +ATOM 1530 C GLY A 193 1.461 -2.183 -1.407 1.00 98.88 C +ATOM 1531 O GLY A 193 0.725 -3.136 -1.175 1.00 98.88 O +ATOM 1532 N GLY A 194 1.545 -1.641 -2.626 1.00 98.81 N +ATOM 1533 CA GLY A 194 0.796 -2.160 -3.776 1.00 98.81 C +ATOM 1534 C GLY A 194 1.232 -3.579 -4.171 1.00 98.81 C +ATOM 1535 O GLY A 194 0.413 -4.409 -4.557 1.00 98.81 O +ATOM 1536 N ASN A 195 2.514 -3.900 -3.994 1.00 98.81 N +ATOM 1537 CA ASN A 195 3.020 -5.262 -4.171 1.00 98.81 C +ATOM 1538 C ASN A 195 2.484 -6.236 -3.101 1.00 98.81 C +ATOM 1539 CB ASN A 195 4.548 -5.183 -4.177 1.00 98.81 C +ATOM 1540 O ASN A 195 2.099 -7.362 -3.427 1.00 98.81 O +ATOM 1541 CG ASN A 195 5.182 -6.541 -4.351 1.00 98.81 C +ATOM 1542 ND2 ASN A 195 5.729 -6.821 -5.502 1.00 98.81 N +ATOM 1543 OD1 ASN A 195 5.215 -7.331 -3.424 1.00 98.81 O +ATOM 1544 N ILE A 196 2.409 -5.802 -1.836 1.00 98.94 N +ATOM 1545 CA ILE A 196 1.782 -6.584 -0.758 1.00 98.94 C +ATOM 1546 C ILE A 196 0.296 -6.800 -1.063 1.00 98.94 C +ATOM 1547 CB ILE A 196 1.996 -5.919 0.624 1.00 98.94 C +ATOM 1548 O ILE A 196 -0.171 -7.933 -0.985 1.00 98.94 O +ATOM 1549 CG1 ILE A 196 3.495 -5.881 0.998 1.00 98.94 C +ATOM 1550 CG2 ILE A 196 1.218 -6.677 1.718 1.00 98.94 C +ATOM 1551 CD1 ILE A 196 3.827 -4.894 2.123 1.00 98.94 C +ATOM 1552 N ALA A 197 -0.425 -5.754 -1.481 1.00 98.88 N +ATOM 1553 CA ALA A 197 -1.835 -5.842 -1.856 1.00 98.88 C +ATOM 1554 C ALA A 197 -2.076 -6.882 -2.966 1.00 98.88 C +ATOM 1555 CB ALA A 197 -2.329 -4.449 -2.267 1.00 98.88 C +ATOM 1556 O ALA A 197 -2.975 -7.706 -2.829 1.00 98.88 O +ATOM 1557 N HIS A 198 -1.235 -6.918 -4.008 1.00 98.44 N +ATOM 1558 CA HIS A 198 -1.289 -7.965 -5.038 1.00 98.44 C +ATOM 1559 C HIS A 198 -1.158 -9.383 -4.449 1.00 98.44 C +ATOM 1560 CB HIS A 198 -0.191 -7.713 -6.086 1.00 98.44 C +ATOM 1561 O HIS A 198 -1.973 -10.257 -4.744 1.00 98.44 O +ATOM 1562 CG HIS A 198 0.031 -8.903 -6.986 1.00 98.44 C +ATOM 1563 CD2 HIS A 198 0.994 -9.867 -6.849 1.00 98.44 C +ATOM 1564 ND1 HIS A 198 -0.763 -9.281 -8.041 1.00 98.44 N +ATOM 1565 CE1 HIS A 198 -0.283 -10.429 -8.541 1.00 98.44 C +ATOM 1566 NE2 HIS A 198 0.791 -10.822 -7.845 1.00 98.44 N +ATOM 1567 N ASN A 199 -0.157 -9.618 -3.596 1.00 98.31 N +ATOM 1568 CA ASN A 199 0.081 -10.945 -3.018 1.00 98.31 C +ATOM 1569 C ASN A 199 -1.046 -11.376 -2.063 1.00 98.31 C +ATOM 1570 CB ASN A 199 1.445 -10.954 -2.310 1.00 98.31 C +ATOM 1571 O ASN A 199 -1.441 -12.542 -2.046 1.00 98.31 O +ATOM 1572 CG ASN A 199 2.585 -11.159 -3.286 1.00 98.31 C +ATOM 1573 ND2 ASN A 199 3.162 -10.103 -3.811 1.00 98.31 N +ATOM 1574 OD1 ASN A 199 2.961 -12.275 -3.594 1.00 98.31 O +ATOM 1575 N VAL A 200 -1.596 -10.437 -1.288 1.00 98.75 N +ATOM 1576 CA VAL A 200 -2.747 -10.692 -0.410 1.00 98.75 C +ATOM 1577 C VAL A 200 -4.007 -10.979 -1.232 1.00 98.75 C +ATOM 1578 CB VAL A 200 -2.943 -9.523 0.574 1.00 98.75 C +ATOM 1579 O VAL A 200 -4.738 -11.908 -0.893 1.00 98.75 O +ATOM 1580 CG1 VAL A 200 -4.215 -9.666 1.416 1.00 98.75 C +ATOM 1581 CG2 VAL A 200 -1.768 -9.441 1.558 1.00 98.75 C +ATOM 1582 N ALA A 201 -4.231 -10.257 -2.336 1.00 98.38 N +ATOM 1583 CA ALA A 201 -5.341 -10.504 -3.259 1.00 98.38 C +ATOM 1584 C ALA A 201 -5.289 -11.905 -3.875 1.00 98.38 C +ATOM 1585 CB ALA A 201 -5.302 -9.463 -4.377 1.00 98.38 C +ATOM 1586 O ALA A 201 -6.306 -12.593 -3.924 1.00 98.38 O +ATOM 1587 N LEU A 202 -4.100 -12.351 -4.294 1.00 96.50 N +ATOM 1588 CA LEU A 202 -3.906 -13.698 -4.826 1.00 96.50 C +ATOM 1589 C LEU A 202 -4.333 -14.759 -3.803 1.00 96.50 C +ATOM 1590 CB LEU A 202 -2.434 -13.849 -5.249 1.00 96.50 C +ATOM 1591 O LEU A 202 -5.111 -15.657 -4.117 1.00 96.50 O +ATOM 1592 CG LEU A 202 -2.059 -15.251 -5.764 1.00 96.50 C +ATOM 1593 CD1 LEU A 202 -2.981 -15.706 -6.892 1.00 96.50 C +ATOM 1594 CD2 LEU A 202 -0.618 -15.228 -6.271 1.00 96.50 C +ATOM 1595 N ARG A 203 -3.888 -14.611 -2.551 1.00 96.81 N +ATOM 1596 CA ARG A 203 -4.241 -15.539 -1.469 1.00 96.81 C +ATOM 1597 C ARG A 203 -5.729 -15.486 -1.100 1.00 96.81 C +ATOM 1598 CB ARG A 203 -3.324 -15.250 -0.276 1.00 96.81 C +ATOM 1599 O ARG A 203 -6.292 -16.515 -0.737 1.00 96.81 O +ATOM 1600 CG ARG A 203 -3.332 -16.384 0.757 1.00 96.81 C +ATOM 1601 CD ARG A 203 -2.211 -16.129 1.770 1.00 96.81 C +ATOM 1602 NE ARG A 203 -2.186 -17.137 2.846 1.00 96.81 N +ATOM 1603 NH1 ARG A 203 -0.316 -16.340 3.941 1.00 96.81 N +ATOM 1604 NH2 ARG A 203 -1.319 -18.199 4.666 1.00 96.81 N +ATOM 1605 CZ ARG A 203 -1.280 -17.216 3.811 1.00 96.81 C +ATOM 1606 N ALA A 204 -6.369 -14.320 -1.212 1.00 97.50 N +ATOM 1607 CA ALA A 204 -7.809 -14.162 -0.994 1.00 97.50 C +ATOM 1608 C ALA A 204 -8.639 -14.917 -2.043 1.00 97.50 C +ATOM 1609 CB ALA A 204 -8.146 -12.667 -0.992 1.00 97.50 C +ATOM 1610 O ALA A 204 -9.573 -15.640 -1.686 1.00 97.50 O +ATOM 1611 N GLY A 205 -8.241 -14.820 -3.316 1.00 94.25 N +ATOM 1612 CA GLY A 205 -8.858 -15.565 -4.413 1.00 94.25 C +ATOM 1613 C GLY A 205 -8.763 -17.081 -4.239 1.00 94.25 C +ATOM 1614 O GLY A 205 -9.719 -17.798 -4.521 1.00 94.25 O +ATOM 1615 N GLU A 206 -7.640 -17.571 -3.707 1.00 92.69 N +ATOM 1616 CA GLU A 206 -7.435 -18.995 -3.400 1.00 92.69 C +ATOM 1617 C GLU A 206 -8.246 -19.469 -2.174 1.00 92.69 C +ATOM 1618 CB GLU A 206 -5.930 -19.265 -3.181 1.00 92.69 C +ATOM 1619 O GLU A 206 -8.589 -20.647 -2.079 1.00 92.69 O +ATOM 1620 CG GLU A 206 -5.059 -19.137 -4.452 1.00 92.69 C +ATOM 1621 CD GLU A 206 -3.544 -19.352 -4.196 1.00 92.69 C +ATOM 1622 OE1 GLU A 206 -2.781 -19.677 -5.145 1.00 92.69 O +ATOM 1623 OE2 GLU A 206 -3.076 -19.222 -3.042 1.00 92.69 O +ATOM 1624 N SER A 207 -8.561 -18.579 -1.226 1.00 94.56 N +ATOM 1625 CA SER A 207 -9.275 -18.932 0.009 1.00 94.56 C +ATOM 1626 C SER A 207 -10.794 -18.763 -0.059 1.00 94.56 C +ATOM 1627 CB SER A 207 -8.746 -18.087 1.164 1.00 94.56 C +ATOM 1628 O SER A 207 -11.489 -19.198 0.858 1.00 94.56 O +ATOM 1629 OG SER A 207 -9.282 -16.779 1.100 1.00 94.56 O +ATOM 1630 N GLY A 208 -11.301 -18.037 -1.059 1.00 90.88 N +ATOM 1631 CA GLY A 208 -12.703 -17.622 -1.148 1.00 90.88 C +ATOM 1632 C GLY A 208 -13.087 -16.447 -0.237 1.00 90.88 C +ATOM 1633 O GLY A 208 -14.272 -16.272 0.040 1.00 90.88 O +ATOM 1634 N ILE A 209 -12.121 -15.655 0.257 1.00 95.88 N +ATOM 1635 CA ILE A 209 -12.450 -14.378 0.919 1.00 95.88 C +ATOM 1636 C ILE A 209 -12.769 -13.370 -0.182 1.00 95.88 C +ATOM 1637 CB ILE A 209 -11.333 -13.869 1.864 1.00 95.88 C +ATOM 1638 O ILE A 209 -11.937 -13.128 -1.053 1.00 95.88 O +ATOM 1639 CG1 ILE A 209 -11.231 -14.793 3.099 1.00 95.88 C +ATOM 1640 CG2 ILE A 209 -11.604 -12.417 2.320 1.00 95.88 C +ATOM 1641 CD1 ILE A 209 -10.105 -14.426 4.077 1.00 95.88 C +ATOM 1642 N ASP A 210 -13.961 -12.784 -0.117 1.00 94.69 N +ATOM 1643 CA ASP A 210 -14.445 -11.829 -1.109 1.00 94.69 C +ATOM 1644 C ASP A 210 -13.737 -10.474 -0.948 1.00 94.69 C +ATOM 1645 CB ASP A 210 -15.974 -11.731 -0.992 1.00 94.69 C +ATOM 1646 O ASP A 210 -13.896 -9.780 0.060 1.00 94.69 O +ATOM 1647 CG ASP A 210 -16.649 -11.073 -2.198 1.00 94.69 C +ATOM 1648 OD1 ASP A 210 -15.964 -10.903 -3.228 1.00 94.69 O +ATOM 1649 OD2 ASP A 210 -17.873 -10.828 -2.075 1.00 94.69 O +ATOM 1650 N VAL A 211 -12.906 -10.131 -1.931 1.00 98.19 N +ATOM 1651 CA VAL A 211 -12.251 -8.826 -2.060 1.00 98.19 C +ATOM 1652 C VAL A 211 -12.838 -8.173 -3.305 1.00 98.19 C +ATOM 1653 CB VAL A 211 -10.717 -8.960 -2.142 1.00 98.19 C +ATOM 1654 O VAL A 211 -12.762 -8.732 -4.393 1.00 98.19 O +ATOM 1655 CG1 VAL A 211 -10.038 -7.608 -2.392 1.00 98.19 C +ATOM 1656 CG2 VAL A 211 -10.134 -9.531 -0.840 1.00 98.19 C +ATOM 1657 N LEU A 212 -13.404 -6.981 -3.147 1.00 98.00 N +ATOM 1658 CA LEU A 212 -14.153 -6.280 -4.192 1.00 98.00 C +ATOM 1659 C LEU A 212 -13.255 -5.493 -5.150 1.00 98.00 C +ATOM 1660 CB LEU A 212 -15.165 -5.338 -3.522 1.00 98.00 C +ATOM 1661 O LEU A 212 -13.649 -5.180 -6.272 1.00 98.00 O +ATOM 1662 CG LEU A 212 -16.160 -6.019 -2.566 1.00 98.00 C +ATOM 1663 CD1 LEU A 212 -17.118 -4.958 -2.035 1.00 98.00 C +ATOM 1664 CD2 LEU A 212 -16.970 -7.128 -3.234 1.00 98.00 C +ATOM 1665 N GLY A 213 -12.035 -5.156 -4.727 1.00 98.62 N +ATOM 1666 CA GLY A 213 -11.101 -4.445 -5.588 1.00 98.62 C +ATOM 1667 C GLY A 213 -9.715 -4.226 -4.991 1.00 98.62 C +ATOM 1668 O GLY A 213 -9.542 -4.160 -3.773 1.00 98.62 O +ATOM 1669 N ASN A 214 -8.736 -4.056 -5.878 1.00 98.50 N +ATOM 1670 CA ASN A 214 -7.368 -3.652 -5.564 1.00 98.50 C +ATOM 1671 C ASN A 214 -7.080 -2.246 -6.092 1.00 98.50 C +ATOM 1672 CB ASN A 214 -6.382 -4.625 -6.213 1.00 98.50 C +ATOM 1673 O ASN A 214 -7.303 -1.964 -7.266 1.00 98.50 O +ATOM 1674 CG ASN A 214 -6.289 -5.973 -5.546 1.00 98.50 C +ATOM 1675 ND2 ASN A 214 -5.769 -6.932 -6.270 1.00 98.50 N +ATOM 1676 OD1 ASN A 214 -6.611 -6.170 -4.390 1.00 98.50 O +ATOM 1677 N ILE A 215 -6.496 -1.394 -5.256 1.00 98.88 N +ATOM 1678 CA ILE A 215 -6.086 -0.030 -5.601 1.00 98.88 C +ATOM 1679 C ILE A 215 -4.569 0.068 -5.448 1.00 98.88 C +ATOM 1680 CB ILE A 215 -6.843 0.993 -4.733 1.00 98.88 C +ATOM 1681 O ILE A 215 -4.038 0.169 -4.340 1.00 98.88 O +ATOM 1682 CG1 ILE A 215 -8.371 0.795 -4.872 1.00 98.88 C +ATOM 1683 CG2 ILE A 215 -6.416 2.422 -5.112 1.00 98.88 C +ATOM 1684 CD1 ILE A 215 -9.193 1.665 -3.926 1.00 98.88 C +ATOM 1685 N LEU A 216 -3.860 -0.003 -6.570 1.00 98.88 N +ATOM 1686 CA LEU A 216 -2.407 -0.135 -6.611 1.00 98.88 C +ATOM 1687 C LEU A 216 -1.781 1.177 -7.092 1.00 98.88 C +ATOM 1688 CB LEU A 216 -2.037 -1.340 -7.497 1.00 98.88 C +ATOM 1689 O LEU A 216 -1.808 1.486 -8.284 1.00 98.88 O +ATOM 1690 CG LEU A 216 -2.739 -2.665 -7.138 1.00 98.88 C +ATOM 1691 CD1 LEU A 216 -2.177 -3.790 -8.003 1.00 98.88 C +ATOM 1692 CD2 LEU A 216 -2.548 -3.066 -5.677 1.00 98.88 C +ATOM 1693 N LEU A 217 -1.215 1.954 -6.166 1.00 98.75 N +ATOM 1694 CA LEU A 217 -0.620 3.262 -6.454 1.00 98.75 C +ATOM 1695 C LEU A 217 0.879 3.121 -6.722 1.00 98.75 C +ATOM 1696 CB LEU A 217 -0.900 4.243 -5.301 1.00 98.75 C +ATOM 1697 O LEU A 217 1.647 2.838 -5.806 1.00 98.75 O +ATOM 1698 CG LEU A 217 -2.376 4.414 -4.910 1.00 98.75 C +ATOM 1699 CD1 LEU A 217 -2.487 5.412 -3.755 1.00 98.75 C +ATOM 1700 CD2 LEU A 217 -3.224 4.915 -6.076 1.00 98.75 C +ATOM 1701 N ASN A 218 1.317 3.307 -7.968 1.00 97.81 N +ATOM 1702 CA ASN A 218 2.715 3.124 -8.370 1.00 97.81 C +ATOM 1703 C ASN A 218 3.323 1.821 -7.802 1.00 97.81 C +ATOM 1704 CB ASN A 218 3.529 4.364 -7.961 1.00 97.81 C +ATOM 1705 O ASN A 218 4.348 1.877 -7.112 1.00 97.81 O +ATOM 1706 CG ASN A 218 3.239 5.598 -8.777 1.00 97.81 C +ATOM 1707 ND2 ASN A 218 4.284 6.275 -9.180 1.00 97.81 N +ATOM 1708 OD1 ASN A 218 2.117 5.990 -9.054 1.00 97.81 O +ATOM 1709 N PRO A 219 2.681 0.650 -7.985 1.00 98.62 N +ATOM 1710 CA PRO A 219 3.086 -0.561 -7.285 1.00 98.62 C +ATOM 1711 C PRO A 219 4.523 -0.963 -7.637 1.00 98.62 C +ATOM 1712 CB PRO A 219 2.061 -1.623 -7.673 1.00 98.62 C +ATOM 1713 O PRO A 219 4.998 -0.773 -8.756 1.00 98.62 O +ATOM 1714 CG PRO A 219 1.589 -1.161 -9.054 1.00 98.62 C +ATOM 1715 CD PRO A 219 1.597 0.361 -8.917 1.00 98.62 C +ATOM 1716 N MET A 220 5.244 -1.501 -6.657 1.00 98.44 N +ATOM 1717 CA MET A 220 6.644 -1.870 -6.822 1.00 98.44 C +ATOM 1718 C MET A 220 6.778 -3.282 -7.391 1.00 98.44 C +ATOM 1719 CB MET A 220 7.399 -1.676 -5.505 1.00 98.44 C +ATOM 1720 O MET A 220 6.556 -4.264 -6.687 1.00 98.44 O +ATOM 1721 CG MET A 220 8.888 -2.005 -5.634 1.00 98.44 C +ATOM 1722 SD MET A 220 9.832 -1.641 -4.133 1.00 98.44 S +ATOM 1723 CE MET A 220 11.427 -2.384 -4.578 1.00 98.44 C +ATOM 1724 N PHE A 221 7.224 -3.369 -8.639 1.00 98.69 N +ATOM 1725 CA PHE A 221 7.624 -4.607 -9.307 1.00 98.69 C +ATOM 1726 C PHE A 221 9.002 -4.426 -9.954 1.00 98.69 C +ATOM 1727 CB PHE A 221 6.539 -5.018 -10.314 1.00 98.69 C +ATOM 1728 O PHE A 221 9.597 -3.344 -9.889 1.00 98.69 O +ATOM 1729 CG PHE A 221 5.182 -5.241 -9.675 1.00 98.69 C +ATOM 1730 CD1 PHE A 221 5.003 -6.283 -8.745 1.00 98.69 C +ATOM 1731 CD2 PHE A 221 4.103 -4.396 -9.991 1.00 98.69 C +ATOM 1732 CE1 PHE A 221 3.755 -6.472 -8.125 1.00 98.69 C +ATOM 1733 CE2 PHE A 221 2.850 -4.603 -9.389 1.00 98.69 C +ATOM 1734 CZ PHE A 221 2.677 -5.632 -8.449 1.00 98.69 C +ATOM 1735 N GLY A 222 9.540 -5.492 -10.525 1.00 98.19 N +ATOM 1736 CA GLY A 222 10.822 -5.490 -11.209 1.00 98.19 C +ATOM 1737 C GLY A 222 11.041 -6.782 -11.982 1.00 98.19 C +ATOM 1738 O GLY A 222 10.137 -7.598 -12.111 1.00 98.19 O +ATOM 1739 N GLY A 223 12.266 -6.949 -12.461 1.00 98.00 N +ATOM 1740 CA GLY A 223 12.765 -8.144 -13.132 1.00 98.00 C +ATOM 1741 C GLY A 223 14.257 -7.984 -13.399 1.00 98.00 C +ATOM 1742 O GLY A 223 14.817 -6.913 -13.146 1.00 98.00 O +ATOM 1743 N ASN A 224 14.934 -9.030 -13.863 1.00 97.00 N +ATOM 1744 CA ASN A 224 16.383 -8.991 -14.068 1.00 97.00 C +ATOM 1745 C ASN A 224 16.788 -8.031 -15.195 1.00 97.00 C +ATOM 1746 CB ASN A 224 16.903 -10.413 -14.331 1.00 97.00 C +ATOM 1747 O ASN A 224 17.745 -7.264 -15.033 1.00 97.00 O +ATOM 1748 CG ASN A 224 17.124 -11.234 -13.071 1.00 97.00 C +ATOM 1749 ND2 ASN A 224 17.380 -12.507 -13.252 1.00 97.00 N +ATOM 1750 OD1 ASN A 224 17.159 -10.731 -11.952 1.00 97.00 O +ATOM 1751 N GLU A 225 16.063 -8.050 -16.312 1.00 96.75 N +ATOM 1752 CA GLU A 225 16.345 -7.192 -17.463 1.00 96.75 C +ATOM 1753 C GLU A 225 15.974 -5.733 -17.179 1.00 96.75 C +ATOM 1754 CB GLU A 225 15.635 -7.704 -18.721 1.00 96.75 C +ATOM 1755 O GLU A 225 14.933 -5.437 -16.605 1.00 96.75 O +ATOM 1756 CG GLU A 225 16.163 -9.085 -19.143 1.00 96.75 C +ATOM 1757 CD GLU A 225 15.690 -9.517 -20.540 1.00 96.75 C +ATOM 1758 OE1 GLU A 225 16.228 -10.538 -21.024 1.00 96.75 O +ATOM 1759 OE2 GLU A 225 14.846 -8.808 -21.133 1.00 96.75 O +ATOM 1760 N ARG A 226 16.829 -4.782 -17.569 1.00 97.81 N +ATOM 1761 CA ARG A 226 16.524 -3.358 -17.378 1.00 97.81 C +ATOM 1762 C ARG A 226 15.590 -2.860 -18.464 1.00 97.81 C +ATOM 1763 CB ARG A 226 17.787 -2.495 -17.299 1.00 97.81 C +ATOM 1764 O ARG A 226 15.917 -2.935 -19.650 1.00 97.81 O +ATOM 1765 CG ARG A 226 18.688 -2.866 -16.121 1.00 97.81 C +ATOM 1766 CD ARG A 226 17.979 -2.728 -14.772 1.00 97.81 C +ATOM 1767 NE ARG A 226 18.895 -3.136 -13.709 1.00 97.81 N +ATOM 1768 NH1 ARG A 226 17.572 -2.646 -11.881 1.00 97.81 N +ATOM 1769 NH2 ARG A 226 19.591 -3.527 -11.627 1.00 97.81 N +ATOM 1770 CZ ARG A 226 18.668 -3.094 -12.419 1.00 97.81 C +ATOM 1771 N THR A 227 14.487 -2.276 -18.027 1.00 98.38 N +ATOM 1772 CA THR A 227 13.520 -1.587 -18.884 1.00 98.38 C +ATOM 1773 C THR A 227 14.049 -0.240 -19.384 1.00 98.38 C +ATOM 1774 CB THR A 227 12.199 -1.398 -18.136 1.00 98.38 C +ATOM 1775 O THR A 227 15.065 0.273 -18.903 1.00 98.38 O +ATOM 1776 CG2 THR A 227 11.533 -2.733 -17.808 1.00 98.38 C +ATOM 1777 OG1 THR A 227 12.415 -0.698 -16.930 1.00 98.38 O +ATOM 1778 N GLU A 228 13.367 0.359 -20.362 1.00 98.12 N +ATOM 1779 CA GLU A 228 13.755 1.670 -20.890 1.00 98.12 C +ATOM 1780 C GLU A 228 13.517 2.783 -19.863 1.00 98.12 C +ATOM 1781 CB GLU A 228 13.019 1.957 -22.209 1.00 98.12 C +ATOM 1782 O GLU A 228 14.334 3.702 -19.761 1.00 98.12 O +ATOM 1783 CG GLU A 228 13.460 1.031 -23.356 1.00 98.12 C +ATOM 1784 CD GLU A 228 14.981 1.080 -23.577 1.00 98.12 C +ATOM 1785 OE1 GLU A 228 15.657 0.040 -23.361 1.00 98.12 O +ATOM 1786 OE2 GLU A 228 15.498 2.180 -23.862 1.00 98.12 O +ATOM 1787 N SER A 229 12.466 2.692 -19.037 1.00 97.94 N +ATOM 1788 CA SER A 229 12.258 3.618 -17.916 1.00 97.94 C +ATOM 1789 C SER A 229 13.384 3.526 -16.887 1.00 97.94 C +ATOM 1790 CB SER A 229 10.884 3.425 -17.257 1.00 97.94 C +ATOM 1791 O SER A 229 13.899 4.561 -16.469 1.00 97.94 O +ATOM 1792 OG SER A 229 10.745 2.206 -16.550 1.00 97.94 O +ATOM 1793 N GLU A 230 13.840 2.318 -16.537 1.00 98.00 N +ATOM 1794 CA GLU A 230 14.961 2.144 -15.604 1.00 98.00 C +ATOM 1795 C GLU A 230 16.252 2.779 -16.133 1.00 98.00 C +ATOM 1796 CB GLU A 230 15.208 0.657 -15.306 1.00 98.00 C +ATOM 1797 O GLU A 230 16.963 3.423 -15.368 1.00 98.00 O +ATOM 1798 CG GLU A 230 14.246 0.066 -14.268 1.00 98.00 C +ATOM 1799 CD GLU A 230 14.501 -1.439 -14.131 1.00 98.00 C +ATOM 1800 OE1 GLU A 230 15.134 -1.869 -13.130 1.00 98.00 O +ATOM 1801 OE2 GLU A 230 14.116 -2.167 -15.076 1.00 98.00 O +ATOM 1802 N LYS A 231 16.549 2.644 -17.433 1.00 97.12 N +ATOM 1803 CA LYS A 231 17.750 3.236 -18.053 1.00 97.12 C +ATOM 1804 C LYS A 231 17.647 4.757 -18.190 1.00 97.12 C +ATOM 1805 CB LYS A 231 17.977 2.621 -19.442 1.00 97.12 C +ATOM 1806 O LYS A 231 18.623 5.467 -17.970 1.00 97.12 O +ATOM 1807 CG LYS A 231 18.302 1.120 -19.416 1.00 97.12 C +ATOM 1808 CD LYS A 231 18.218 0.557 -20.842 1.00 97.12 C +ATOM 1809 CE LYS A 231 18.091 -0.965 -20.818 1.00 97.12 C +ATOM 1810 NZ LYS A 231 17.565 -1.467 -22.109 1.00 97.12 N +ATOM 1811 N SER A 232 16.482 5.262 -18.594 1.00 94.94 N +ATOM 1812 CA SER A 232 16.302 6.671 -18.972 1.00 94.94 C +ATOM 1813 C SER A 232 15.945 7.598 -17.810 1.00 94.94 C +ATOM 1814 CB SER A 232 15.256 6.794 -20.082 1.00 94.94 C +ATOM 1815 O SER A 232 16.150 8.812 -17.930 1.00 94.94 O +ATOM 1816 OG SER A 232 13.990 6.359 -19.635 1.00 94.94 O +ATOM 1817 N LEU A 233 15.422 7.059 -16.703 1.00 95.00 N +ATOM 1818 CA LEU A 233 14.981 7.823 -15.530 1.00 95.00 C +ATOM 1819 C LEU A 233 15.845 7.600 -14.282 1.00 95.00 C +ATOM 1820 CB LEU A 233 13.490 7.549 -15.241 1.00 95.00 C +ATOM 1821 O LEU A 233 15.534 8.188 -13.243 1.00 95.00 O +ATOM 1822 CG LEU A 233 12.524 7.910 -16.385 1.00 95.00 C +ATOM 1823 CD1 LEU A 233 11.122 7.410 -16.040 1.00 95.00 C +ATOM 1824 CD2 LEU A 233 12.458 9.427 -16.637 1.00 95.00 C +ATOM 1825 N ASP A 234 16.922 6.808 -14.364 1.00 95.56 N +ATOM 1826 CA ASP A 234 17.805 6.540 -13.222 1.00 95.56 C +ATOM 1827 C ASP A 234 18.294 7.841 -12.568 1.00 95.56 C +ATOM 1828 CB ASP A 234 18.992 5.634 -13.601 1.00 95.56 C +ATOM 1829 O ASP A 234 18.950 8.675 -13.190 1.00 95.56 O +ATOM 1830 CG ASP A 234 19.712 5.072 -12.364 1.00 95.56 C +ATOM 1831 OD1 ASP A 234 19.149 5.164 -11.244 1.00 95.56 O +ATOM 1832 OD2 ASP A 234 20.812 4.501 -12.507 1.00 95.56 O +ATOM 1833 N GLY A 235 17.902 8.056 -11.310 1.00 89.19 N +ATOM 1834 CA GLY A 235 18.277 9.229 -10.524 1.00 89.19 C +ATOM 1835 C GLY A 235 17.564 10.540 -10.870 1.00 89.19 C +ATOM 1836 O GLY A 235 17.722 11.495 -10.103 1.00 89.19 O +ATOM 1837 N LYS A 236 16.746 10.602 -11.934 1.00 87.94 N +ATOM 1838 CA LYS A 236 16.081 11.844 -12.384 1.00 87.94 C +ATOM 1839 C LYS A 236 14.983 12.318 -11.432 1.00 87.94 C +ATOM 1840 CB LYS A 236 15.517 11.686 -13.806 1.00 87.94 C +ATOM 1841 O LYS A 236 14.948 13.488 -11.067 1.00 87.94 O +ATOM 1842 CG LYS A 236 16.620 11.516 -14.861 1.00 87.94 C +ATOM 1843 CD LYS A 236 16.051 11.696 -16.274 1.00 87.94 C +ATOM 1844 CE LYS A 236 17.168 11.551 -17.307 1.00 87.94 C +ATOM 1845 NZ LYS A 236 16.629 11.574 -18.684 1.00 87.94 N +ATOM 1846 N TYR A 237 14.118 11.408 -10.982 1.00 85.81 N +ATOM 1847 CA TYR A 237 12.978 11.713 -10.107 1.00 85.81 C +ATOM 1848 C TYR A 237 13.087 10.952 -8.781 1.00 85.81 C +ATOM 1849 CB TYR A 237 11.654 11.485 -10.850 1.00 85.81 C +ATOM 1850 O TYR A 237 12.261 10.109 -8.440 1.00 85.81 O +ATOM 1851 CG TYR A 237 11.497 12.354 -12.083 1.00 85.81 C +ATOM 1852 CD1 TYR A 237 11.144 13.711 -11.952 1.00 85.81 C +ATOM 1853 CD2 TYR A 237 11.717 11.806 -13.362 1.00 85.81 C +ATOM 1854 CE1 TYR A 237 10.984 14.511 -13.098 1.00 85.81 C +ATOM 1855 CE2 TYR A 237 11.591 12.613 -14.509 1.00 85.81 C +ATOM 1856 OH TYR A 237 11.060 14.753 -15.474 1.00 85.81 O +ATOM 1857 CZ TYR A 237 11.217 13.968 -14.378 1.00 85.81 C +ATOM 1858 N PHE A 238 14.134 11.275 -8.015 1.00 86.44 N +ATOM 1859 CA PHE A 238 14.406 10.818 -6.639 1.00 86.44 C +ATOM 1860 C PHE A 238 14.895 9.375 -6.466 1.00 86.44 C +ATOM 1861 CB PHE A 238 13.199 11.081 -5.722 1.00 86.44 C +ATOM 1862 O PHE A 238 15.769 9.152 -5.626 1.00 86.44 O +ATOM 1863 CG PHE A 238 12.533 12.424 -5.936 1.00 86.44 C +ATOM 1864 CD1 PHE A 238 13.156 13.593 -5.463 1.00 86.44 C +ATOM 1865 CD2 PHE A 238 11.307 12.508 -6.625 1.00 86.44 C +ATOM 1866 CE1 PHE A 238 12.565 14.843 -5.699 1.00 86.44 C +ATOM 1867 CE2 PHE A 238 10.718 13.758 -6.863 1.00 86.44 C +ATOM 1868 CZ PHE A 238 11.352 14.923 -6.404 1.00 86.44 C +ATOM 1869 N VAL A 239 14.371 8.425 -7.239 1.00 91.81 N +ATOM 1870 CA VAL A 239 14.699 6.993 -7.135 1.00 91.81 C +ATOM 1871 C VAL A 239 15.871 6.639 -8.052 1.00 91.81 C +ATOM 1872 CB VAL A 239 13.460 6.121 -7.435 1.00 91.81 C +ATOM 1873 O VAL A 239 15.979 7.178 -9.151 1.00 91.81 O +ATOM 1874 CG1 VAL A 239 13.727 4.631 -7.172 1.00 91.81 C +ATOM 1875 CG2 VAL A 239 12.271 6.528 -6.551 1.00 91.81 C +ATOM 1876 N THR A 240 16.739 5.726 -7.608 1.00 95.25 N +ATOM 1877 CA THR A 240 17.804 5.149 -8.442 1.00 95.25 C +ATOM 1878 C THR A 240 17.645 3.640 -8.602 1.00 95.25 C +ATOM 1879 CB THR A 240 19.216 5.465 -7.920 1.00 95.25 C +ATOM 1880 O THR A 240 17.064 2.969 -7.738 1.00 95.25 O +ATOM 1881 CG2 THR A 240 19.459 6.947 -7.640 1.00 95.25 C +ATOM 1882 OG1 THR A 240 19.488 4.764 -6.728 1.00 95.25 O +ATOM 1883 N VAL A 241 18.213 3.093 -9.676 1.00 97.31 N +ATOM 1884 CA VAL A 241 18.337 1.651 -9.917 1.00 97.31 C +ATOM 1885 C VAL A 241 19.086 0.984 -8.766 1.00 97.31 C +ATOM 1886 CB VAL A 241 19.052 1.406 -11.262 1.00 97.31 C +ATOM 1887 O VAL A 241 18.596 0.013 -8.191 1.00 97.31 O +ATOM 1888 CG1 VAL A 241 19.576 -0.027 -11.373 1.00 97.31 C +ATOM 1889 CG2 VAL A 241 18.094 1.674 -12.428 1.00 97.31 C +ATOM 1890 N ARG A 242 20.218 1.565 -8.348 1.00 95.62 N +ATOM 1891 CA ARG A 242 21.010 1.076 -7.208 1.00 95.62 C +ATOM 1892 C ARG A 242 20.164 0.933 -5.944 1.00 95.62 C +ATOM 1893 CB ARG A 242 22.183 2.038 -6.975 1.00 95.62 C +ATOM 1894 O ARG A 242 20.331 -0.025 -5.189 1.00 95.62 O +ATOM 1895 CG ARG A 242 23.058 1.598 -5.793 1.00 95.62 C +ATOM 1896 CD ARG A 242 24.196 2.591 -5.569 1.00 95.62 C +ATOM 1897 NE ARG A 242 25.010 2.195 -4.407 1.00 95.62 N +ATOM 1898 NH1 ARG A 242 26.523 3.913 -4.518 1.00 95.62 N +ATOM 1899 NH2 ARG A 242 26.735 2.354 -2.932 1.00 95.62 N +ATOM 1900 CZ ARG A 242 26.080 2.822 -3.957 1.00 95.62 C +ATOM 1901 N ASP A 243 19.266 1.883 -5.698 1.00 95.62 N +ATOM 1902 CA ASP A 243 18.389 1.826 -4.538 1.00 95.62 C +ATOM 1903 C ASP A 243 17.364 0.708 -4.659 1.00 95.62 C +ATOM 1904 CB ASP A 243 17.699 3.175 -4.303 1.00 95.62 C +ATOM 1905 O ASP A 243 17.189 -0.041 -3.700 1.00 95.62 O +ATOM 1906 CG ASP A 243 18.691 4.273 -3.951 1.00 95.62 C +ATOM 1907 OD1 ASP A 243 19.687 3.947 -3.266 1.00 95.62 O +ATOM 1908 OD2 ASP A 243 18.422 5.437 -4.316 1.00 95.62 O +ATOM 1909 N ARG A 244 16.717 0.565 -5.824 1.00 96.31 N +ATOM 1910 CA ARG A 244 15.780 -0.540 -6.080 1.00 96.31 C +ATOM 1911 C ARG A 244 16.446 -1.896 -5.884 1.00 96.31 C +ATOM 1912 CB ARG A 244 15.199 -0.460 -7.503 1.00 96.31 C +ATOM 1913 O ARG A 244 15.865 -2.742 -5.212 1.00 96.31 O +ATOM 1914 CG ARG A 244 14.241 0.706 -7.778 1.00 96.31 C +ATOM 1915 CD ARG A 244 13.044 0.756 -6.821 1.00 96.31 C +ATOM 1916 NE ARG A 244 13.375 1.518 -5.608 1.00 96.31 N +ATOM 1917 NH1 ARG A 244 11.692 0.765 -4.239 1.00 96.31 N +ATOM 1918 NH2 ARG A 244 13.054 2.342 -3.529 1.00 96.31 N +ATOM 1919 CZ ARG A 244 12.708 1.529 -4.476 1.00 96.31 C +ATOM 1920 N ASP A 245 17.660 -2.070 -6.395 1.00 96.94 N +ATOM 1921 CA ASP A 245 18.417 -3.314 -6.255 1.00 96.94 C +ATOM 1922 C ASP A 245 18.719 -3.628 -4.790 1.00 96.94 C +ATOM 1923 CB ASP A 245 19.734 -3.210 -7.025 1.00 96.94 C +ATOM 1924 O ASP A 245 18.546 -4.763 -4.349 1.00 96.94 O +ATOM 1925 CG ASP A 245 19.562 -3.099 -8.535 1.00 96.94 C +ATOM 1926 OD1 ASP A 245 18.454 -3.321 -9.082 1.00 96.94 O +ATOM 1927 OD2 ASP A 245 20.579 -2.818 -9.199 1.00 96.94 O +ATOM 1928 N TRP A 246 19.111 -2.614 -4.010 1.00 97.06 N +ATOM 1929 CA TRP A 246 19.338 -2.781 -2.577 1.00 97.06 C +ATOM 1930 C TRP A 246 18.060 -3.234 -1.858 1.00 97.06 C +ATOM 1931 CB TRP A 246 19.909 -1.483 -1.990 1.00 97.06 C +ATOM 1932 O TRP A 246 18.109 -4.168 -1.063 1.00 97.06 O +ATOM 1933 CG TRP A 246 20.400 -1.599 -0.578 1.00 97.06 C +ATOM 1934 CD1 TRP A 246 21.694 -1.718 -0.200 1.00 97.06 C +ATOM 1935 CD2 TRP A 246 19.619 -1.685 0.653 1.00 97.06 C +ATOM 1936 CE2 TRP A 246 20.517 -1.877 1.745 1.00 97.06 C +ATOM 1937 CE3 TRP A 246 18.240 -1.620 0.957 1.00 97.06 C +ATOM 1938 NE1 TRP A 246 21.771 -1.843 1.175 1.00 97.06 N +ATOM 1939 CH2 TRP A 246 18.686 -2.058 3.309 1.00 97.06 C +ATOM 1940 CZ2 TRP A 246 20.067 -2.051 3.058 1.00 97.06 C +ATOM 1941 CZ3 TRP A 246 17.776 -1.813 2.270 1.00 97.06 C +ATOM 1942 N TYR A 247 16.907 -2.616 -2.148 1.00 97.81 N +ATOM 1943 CA TYR A 247 15.643 -2.980 -1.495 1.00 97.81 C +ATOM 1944 C TYR A 247 15.137 -4.361 -1.905 1.00 97.81 C +ATOM 1945 CB TYR A 247 14.559 -1.937 -1.772 1.00 97.81 C +ATOM 1946 O TYR A 247 14.642 -5.091 -1.051 1.00 97.81 O +ATOM 1947 CG TYR A 247 14.823 -0.591 -1.138 1.00 97.81 C +ATOM 1948 CD1 TYR A 247 14.905 -0.469 0.261 1.00 97.81 C +ATOM 1949 CD2 TYR A 247 14.970 0.543 -1.949 1.00 97.81 C +ATOM 1950 CE1 TYR A 247 15.180 0.782 0.840 1.00 97.81 C +ATOM 1951 CE2 TYR A 247 15.252 1.794 -1.381 1.00 97.81 C +ATOM 1952 OH TYR A 247 15.678 3.125 0.531 1.00 97.81 O +ATOM 1953 CZ TYR A 247 15.368 1.912 0.013 1.00 97.81 C +ATOM 1954 N TRP A 248 15.287 -4.742 -3.176 1.00 98.31 N +ATOM 1955 CA TRP A 248 14.953 -6.098 -3.607 1.00 98.31 C +ATOM 1956 C TRP A 248 15.846 -7.129 -2.926 1.00 98.31 C +ATOM 1957 CB TRP A 248 14.997 -6.216 -5.133 1.00 98.31 C +ATOM 1958 O TRP A 248 15.322 -8.092 -2.380 1.00 98.31 O +ATOM 1959 CG TRP A 248 13.780 -5.677 -5.817 1.00 98.31 C +ATOM 1960 CD1 TRP A 248 13.758 -4.744 -6.796 1.00 98.31 C +ATOM 1961 CD2 TRP A 248 12.386 -6.047 -5.581 1.00 98.31 C +ATOM 1962 CE2 TRP A 248 11.566 -5.271 -6.453 1.00 98.31 C +ATOM 1963 CE3 TRP A 248 11.729 -6.962 -4.727 1.00 98.31 C +ATOM 1964 NE1 TRP A 248 12.452 -4.519 -7.194 1.00 98.31 N +ATOM 1965 CH2 TRP A 248 9.547 -6.264 -5.568 1.00 98.31 C +ATOM 1966 CZ2 TRP A 248 10.171 -5.383 -6.466 1.00 98.31 C +ATOM 1967 CZ3 TRP A 248 10.325 -7.064 -4.715 1.00 98.31 C +ATOM 1968 N LYS A 249 17.160 -6.891 -2.849 1.00 97.50 N +ATOM 1969 CA LYS A 249 18.079 -7.777 -2.121 1.00 97.50 C +ATOM 1970 C LYS A 249 17.737 -7.890 -0.630 1.00 97.50 C +ATOM 1971 CB LYS A 249 19.519 -7.292 -2.335 1.00 97.50 C +ATOM 1972 O LYS A 249 17.895 -8.956 -0.056 1.00 97.50 O +ATOM 1973 CG LYS A 249 20.523 -8.276 -1.721 1.00 97.50 C +ATOM 1974 CD LYS A 249 21.973 -7.856 -1.969 1.00 97.50 C +ATOM 1975 CE LYS A 249 22.872 -8.913 -1.318 1.00 97.50 C +ATOM 1976 NZ LYS A 249 24.311 -8.587 -1.450 1.00 97.50 N +ATOM 1977 N ALA A 250 17.286 -6.803 -0.002 1.00 97.62 N +ATOM 1978 CA ALA A 250 16.892 -6.806 1.406 1.00 97.62 C +ATOM 1979 C ALA A 250 15.580 -7.567 1.667 1.00 97.62 C +ATOM 1980 CB ALA A 250 16.781 -5.348 1.871 1.00 97.62 C +ATOM 1981 O ALA A 250 15.386 -8.081 2.766 1.00 97.62 O +ATOM 1982 N PHE A 251 14.661 -7.590 0.696 1.00 98.69 N +ATOM 1983 CA PHE A 251 13.343 -8.201 0.857 1.00 98.69 C +ATOM 1984 C PHE A 251 13.274 -9.649 0.368 1.00 98.69 C +ATOM 1985 CB PHE A 251 12.295 -7.333 0.151 1.00 98.69 C +ATOM 1986 O PHE A 251 12.602 -10.458 0.998 1.00 98.69 O +ATOM 1987 CG PHE A 251 10.893 -7.905 0.236 1.00 98.69 C +ATOM 1988 CD1 PHE A 251 10.263 -8.407 -0.916 1.00 98.69 C +ATOM 1989 CD2 PHE A 251 10.234 -7.982 1.478 1.00 98.69 C +ATOM 1990 CE1 PHE A 251 8.977 -8.963 -0.822 1.00 98.69 C +ATOM 1991 CE2 PHE A 251 8.952 -8.550 1.572 1.00 98.69 C +ATOM 1992 CZ PHE A 251 8.320 -9.040 0.418 1.00 98.69 C +ATOM 1993 N LEU A 252 13.913 -9.984 -0.752 1.00 98.75 N +ATOM 1994 CA LEU A 252 13.849 -11.325 -1.333 1.00 98.75 C +ATOM 1995 C LEU A 252 14.556 -12.365 -0.446 1.00 98.75 C +ATOM 1996 CB LEU A 252 14.445 -11.291 -2.750 1.00 98.75 C +ATOM 1997 O LEU A 252 15.431 -11.996 0.342 1.00 98.75 O +ATOM 1998 CG LEU A 252 13.613 -10.480 -3.759 1.00 98.75 C +ATOM 1999 CD1 LEU A 252 14.368 -10.387 -5.081 1.00 98.75 C +ATOM 2000 CD2 LEU A 252 12.243 -11.112 -4.009 1.00 98.75 C +ATOM 2001 N PRO A 253 14.201 -13.661 -0.564 1.00 98.19 N +ATOM 2002 CA PRO A 253 14.959 -14.724 0.088 1.00 98.19 C +ATOM 2003 C PRO A 253 16.447 -14.652 -0.269 1.00 98.19 C +ATOM 2004 CB PRO A 253 14.348 -16.045 -0.385 1.00 98.19 C +ATOM 2005 O PRO A 253 16.822 -14.244 -1.369 1.00 98.19 O +ATOM 2006 CG PRO A 253 12.944 -15.648 -0.828 1.00 98.19 C +ATOM 2007 CD PRO A 253 13.121 -14.229 -1.361 1.00 98.19 C +ATOM 2008 N GLU A 254 17.311 -15.069 0.651 1.00 95.88 N +ATOM 2009 CA GLU A 254 18.747 -15.080 0.385 1.00 95.88 C +ATOM 2010 C GLU A 254 19.077 -15.981 -0.818 1.00 95.88 C +ATOM 2011 CB GLU A 254 19.510 -15.489 1.650 1.00 95.88 C +ATOM 2012 O GLU A 254 18.626 -17.121 -0.902 1.00 95.88 O +ATOM 2013 CG GLU A 254 21.027 -15.360 1.451 1.00 95.88 C +ATOM 2014 CD GLU A 254 21.829 -15.542 2.748 1.00 95.88 C +ATOM 2015 OE1 GLU A 254 23.015 -15.140 2.730 1.00 95.88 O +ATOM 2016 OE2 GLU A 254 21.267 -16.058 3.740 1.00 95.88 O +ATOM 2017 N GLY A 255 19.862 -15.450 -1.760 1.00 95.50 N +ATOM 2018 CA GLY A 255 20.242 -16.142 -2.995 1.00 95.50 C +ATOM 2019 C GLY A 255 19.240 -16.014 -4.146 1.00 95.50 C +ATOM 2020 O GLY A 255 19.580 -16.386 -5.267 1.00 95.50 O +ATOM 2021 N GLU A 256 18.051 -15.457 -3.909 1.00 97.75 N +ATOM 2022 CA GLU A 256 17.075 -15.200 -4.968 1.00 97.75 C +ATOM 2023 C GLU A 256 17.405 -13.947 -5.787 1.00 97.75 C +ATOM 2024 CB GLU A 256 15.652 -15.132 -4.390 1.00 97.75 C +ATOM 2025 O GLU A 256 18.008 -12.983 -5.306 1.00 97.75 O +ATOM 2026 CG GLU A 256 15.101 -16.527 -4.050 1.00 97.75 C +ATOM 2027 CD GLU A 256 14.834 -17.372 -5.303 1.00 97.75 C +ATOM 2028 OE1 GLU A 256 14.581 -18.588 -5.189 1.00 97.75 O +ATOM 2029 OE2 GLU A 256 14.795 -16.796 -6.418 1.00 97.75 O +ATOM 2030 N ASP A 257 16.977 -13.965 -7.048 1.00 97.06 N +ATOM 2031 CA ASP A 257 17.114 -12.855 -7.985 1.00 97.06 C +ATOM 2032 C ASP A 257 15.771 -12.159 -8.256 1.00 97.06 C +ATOM 2033 CB ASP A 257 17.822 -13.339 -9.260 1.00 97.06 C +ATOM 2034 O ASP A 257 14.733 -12.468 -7.665 1.00 97.06 O +ATOM 2035 CG ASP A 257 16.960 -14.168 -10.221 1.00 97.06 C +ATOM 2036 OD1 ASP A 257 15.753 -14.385 -9.957 1.00 97.06 O +ATOM 2037 OD2 ASP A 257 17.504 -14.506 -11.291 1.00 97.06 O +ATOM 2038 N ARG A 258 15.774 -11.188 -9.173 1.00 97.62 N +ATOM 2039 CA ARG A 258 14.582 -10.385 -9.464 1.00 97.62 C +ATOM 2040 C ARG A 258 13.571 -11.094 -10.369 1.00 97.62 C +ATOM 2041 CB ARG A 258 14.986 -8.986 -9.948 1.00 97.62 C +ATOM 2042 O ARG A 258 12.510 -10.532 -10.599 1.00 97.62 O +ATOM 2043 CG ARG A 258 15.737 -8.229 -8.837 1.00 97.62 C +ATOM 2044 CD ARG A 258 16.015 -6.774 -9.216 1.00 97.62 C +ATOM 2045 NE ARG A 258 16.902 -6.678 -10.392 1.00 97.62 N +ATOM 2046 NH1 ARG A 258 15.892 -4.864 -11.355 1.00 97.62 N +ATOM 2047 NH2 ARG A 258 17.506 -5.948 -12.458 1.00 97.62 N +ATOM 2048 CZ ARG A 258 16.777 -5.821 -11.386 1.00 97.62 C +ATOM 2049 N GLU A 259 13.820 -12.337 -10.791 1.00 97.62 N +ATOM 2050 CA GLU A 259 12.781 -13.193 -11.390 1.00 97.62 C +ATOM 2051 C GLU A 259 11.919 -13.898 -10.339 1.00 97.62 C +ATOM 2052 CB GLU A 259 13.381 -14.224 -12.364 1.00 97.62 C +ATOM 2053 O GLU A 259 11.017 -14.668 -10.688 1.00 97.62 O +ATOM 2054 CG GLU A 259 14.086 -13.574 -13.556 1.00 97.62 C +ATOM 2055 CD GLU A 259 13.201 -12.551 -14.275 1.00 97.62 C +ATOM 2056 OE1 GLU A 259 13.713 -11.450 -14.582 1.00 97.62 O +ATOM 2057 OE2 GLU A 259 11.989 -12.824 -14.443 1.00 97.62 O +ATOM 2058 N HIS A 260 12.191 -13.677 -9.049 1.00 98.50 N +ATOM 2059 CA HIS A 260 11.384 -14.240 -7.978 1.00 98.50 C +ATOM 2060 C HIS A 260 9.967 -13.650 -8.060 1.00 98.50 C +ATOM 2061 CB HIS A 260 12.060 -13.977 -6.627 1.00 98.50 C +ATOM 2062 O HIS A 260 9.848 -12.436 -8.243 1.00 98.50 O +ATOM 2063 CG HIS A 260 11.320 -14.606 -5.475 1.00 98.50 C +ATOM 2064 CD2 HIS A 260 11.527 -15.848 -4.940 1.00 98.50 C +ATOM 2065 ND1 HIS A 260 10.271 -14.042 -4.791 1.00 98.50 N +ATOM 2066 CE1 HIS A 260 9.844 -14.924 -3.875 1.00 98.50 C +ATOM 2067 NE2 HIS A 260 10.579 -16.040 -3.928 1.00 98.50 N +ATOM 2068 N PRO A 261 8.887 -14.435 -7.874 1.00 98.12 N +ATOM 2069 CA PRO A 261 7.505 -13.961 -8.057 1.00 98.12 C +ATOM 2070 C PRO A 261 7.120 -12.736 -7.219 1.00 98.12 C +ATOM 2071 CB PRO A 261 6.623 -15.157 -7.698 1.00 98.12 C +ATOM 2072 O PRO A 261 6.280 -11.938 -7.615 1.00 98.12 O +ATOM 2073 CG PRO A 261 7.502 -16.338 -8.089 1.00 98.12 C +ATOM 2074 CD PRO A 261 8.896 -15.880 -7.686 1.00 98.12 C +ATOM 2075 N ALA A 262 7.768 -12.546 -6.068 1.00 98.25 N +ATOM 2076 CA ALA A 262 7.610 -11.335 -5.262 1.00 98.25 C +ATOM 2077 C ALA A 262 8.060 -10.053 -5.997 1.00 98.25 C +ATOM 2078 CB ALA A 262 8.416 -11.520 -3.975 1.00 98.25 C +ATOM 2079 O ALA A 262 7.486 -8.987 -5.788 1.00 98.25 O +ATOM 2080 N CYS A 263 9.093 -10.151 -6.840 1.00 98.62 N +ATOM 2081 CA CYS A 263 9.625 -9.057 -7.649 1.00 98.62 C +ATOM 2082 C CYS A 263 8.945 -8.977 -9.017 1.00 98.62 C +ATOM 2083 CB CYS A 263 11.144 -9.236 -7.772 1.00 98.62 C +ATOM 2084 O CYS A 263 8.474 -7.903 -9.400 1.00 98.62 O +ATOM 2085 SG CYS A 263 11.865 -7.760 -8.534 1.00 98.62 S +ATOM 2086 N ASN A 264 8.845 -10.122 -9.693 1.00 98.31 N +ATOM 2087 CA ASN A 264 8.268 -10.273 -11.021 1.00 98.31 C +ATOM 2088 C ASN A 264 7.106 -11.289 -10.984 1.00 98.31 C +ATOM 2089 CB ASN A 264 9.394 -10.688 -11.988 1.00 98.31 C +ATOM 2090 O ASN A 264 7.326 -12.474 -11.249 1.00 98.31 O +ATOM 2091 CG ASN A 264 8.943 -10.631 -13.439 1.00 98.31 C +ATOM 2092 ND2 ASN A 264 9.806 -10.942 -14.379 1.00 98.31 N +ATOM 2093 OD1 ASN A 264 7.795 -10.337 -13.749 1.00 98.31 O +ATOM 2094 N PRO A 265 5.870 -10.868 -10.647 1.00 97.81 N +ATOM 2095 CA PRO A 265 4.740 -11.787 -10.473 1.00 97.81 C +ATOM 2096 C PRO A 265 4.374 -12.597 -11.718 1.00 97.81 C +ATOM 2097 CB PRO A 265 3.557 -10.915 -10.052 1.00 97.81 C +ATOM 2098 O PRO A 265 3.818 -13.683 -11.592 1.00 97.81 O +ATOM 2099 CG PRO A 265 4.210 -9.699 -9.409 1.00 97.81 C +ATOM 2100 CD PRO A 265 5.497 -9.529 -10.213 1.00 97.81 C +ATOM 2101 N PHE A 266 4.696 -12.076 -12.906 1.00 96.75 N +ATOM 2102 CA PHE A 266 4.422 -12.709 -14.197 1.00 96.75 C +ATOM 2103 C PHE A 266 5.684 -13.291 -14.854 1.00 96.75 C +ATOM 2104 CB PHE A 266 3.676 -11.721 -15.107 1.00 96.75 C +ATOM 2105 O PHE A 266 5.687 -13.552 -16.058 1.00 96.75 O +ATOM 2106 CG PHE A 266 2.321 -11.293 -14.580 1.00 96.75 C +ATOM 2107 CD1 PHE A 266 1.245 -12.201 -14.599 1.00 96.75 C +ATOM 2108 CD2 PHE A 266 2.129 -9.991 -14.081 1.00 96.75 C +ATOM 2109 CE1 PHE A 266 -0.018 -11.810 -14.123 1.00 96.75 C +ATOM 2110 CE2 PHE A 266 0.864 -9.602 -13.607 1.00 96.75 C +ATOM 2111 CZ PHE A 266 -0.204 -10.511 -13.623 1.00 96.75 C +ATOM 2112 N SER A 267 6.762 -13.500 -14.089 1.00 95.62 N +ATOM 2113 CA SER A 267 7.899 -14.296 -14.561 1.00 95.62 C +ATOM 2114 C SER A 267 7.473 -15.753 -14.802 1.00 95.62 C +ATOM 2115 CB SER A 267 9.072 -14.234 -13.569 1.00 95.62 C +ATOM 2116 O SER A 267 6.442 -16.188 -14.285 1.00 95.62 O +ATOM 2117 OG SER A 267 8.929 -15.136 -12.484 1.00 95.62 O +ATOM 2118 N PRO A 268 8.284 -16.574 -15.495 1.00 94.38 N +ATOM 2119 CA PRO A 268 8.026 -18.012 -15.615 1.00 94.38 C +ATOM 2120 C PRO A 268 7.926 -18.760 -14.274 1.00 94.38 C +ATOM 2121 CB PRO A 268 9.192 -18.555 -16.447 1.00 94.38 C +ATOM 2122 O PRO A 268 7.427 -19.881 -14.237 1.00 94.38 O +ATOM 2123 CG PRO A 268 9.644 -17.348 -17.267 1.00 94.38 C +ATOM 2124 CD PRO A 268 9.435 -16.187 -16.298 1.00 94.38 C +ATOM 2125 N ARG A 269 8.425 -18.167 -13.177 1.00 95.38 N +ATOM 2126 CA ARG A 269 8.325 -18.717 -11.814 1.00 95.38 C +ATOM 2127 C ARG A 269 7.050 -18.273 -11.093 1.00 95.38 C +ATOM 2128 CB ARG A 269 9.568 -18.329 -10.992 1.00 95.38 C +ATOM 2129 O ARG A 269 6.733 -18.813 -10.034 1.00 95.38 O +ATOM 2130 CG ARG A 269 10.879 -18.720 -11.691 1.00 95.38 C +ATOM 2131 CD ARG A 269 12.072 -18.719 -10.733 1.00 95.38 C +ATOM 2132 NE ARG A 269 12.479 -17.378 -10.252 1.00 95.38 N +ATOM 2133 NH1 ARG A 269 13.575 -18.148 -8.392 1.00 95.38 N +ATOM 2134 NH2 ARG A 269 13.811 -16.061 -8.887 1.00 95.38 N +ATOM 2135 CZ ARG A 269 13.270 -17.188 -9.207 1.00 95.38 C +ATOM 2136 N GLY A 270 6.365 -17.264 -11.626 1.00 93.88 N +ATOM 2137 CA GLY A 270 5.106 -16.747 -11.118 1.00 93.88 C +ATOM 2138 C GLY A 270 3.963 -17.741 -11.286 1.00 93.88 C +ATOM 2139 O GLY A 270 3.978 -18.609 -12.160 1.00 93.88 O +ATOM 2140 N LYS A 271 2.944 -17.618 -10.435 1.00 89.50 N +ATOM 2141 CA LYS A 271 1.695 -18.362 -10.613 1.00 89.50 C +ATOM 2142 C LYS A 271 0.862 -17.682 -11.698 1.00 89.50 C +ATOM 2143 CB LYS A 271 0.910 -18.432 -9.300 1.00 89.50 C +ATOM 2144 O LYS A 271 0.693 -16.465 -11.664 1.00 89.50 O +ATOM 2145 CG LYS A 271 1.524 -19.369 -8.244 1.00 89.50 C +ATOM 2146 CD LYS A 271 0.600 -19.343 -7.018 1.00 89.50 C +ATOM 2147 CE LYS A 271 0.923 -20.362 -5.924 1.00 89.50 C +ATOM 2148 NZ LYS A 271 -0.115 -20.274 -4.856 1.00 89.50 N +ATOM 2149 N SER A 272 0.297 -18.466 -12.617 1.00 90.38 N +ATOM 2150 CA SER A 272 -0.724 -17.943 -13.530 1.00 90.38 C +ATOM 2151 C SER A 272 -1.941 -17.473 -12.735 1.00 90.38 C +ATOM 2152 CB SER A 272 -1.149 -18.978 -14.576 1.00 90.38 C +ATOM 2153 O SER A 272 -2.347 -18.122 -11.771 1.00 90.38 O +ATOM 2154 OG SER A 272 -2.060 -18.363 -15.467 1.00 90.38 O +ATOM 2155 N LEU A 273 -2.520 -16.351 -13.157 1.00 94.00 N +ATOM 2156 CA LEU A 273 -3.787 -15.850 -12.626 1.00 94.00 C +ATOM 2157 C LEU A 273 -4.978 -16.273 -13.493 1.00 94.00 C +ATOM 2158 CB LEU A 273 -3.716 -14.326 -12.432 1.00 94.00 C +ATOM 2159 O LEU A 273 -6.104 -15.897 -13.189 1.00 94.00 O +ATOM 2160 CG LEU A 273 -2.599 -13.847 -11.494 1.00 94.00 C +ATOM 2161 CD1 LEU A 273 -2.638 -12.325 -11.430 1.00 94.00 C +ATOM 2162 CD2 LEU A 273 -2.758 -14.385 -10.079 1.00 94.00 C +ATOM 2163 N GLU A 274 -4.768 -17.038 -14.565 1.00 94.50 N +ATOM 2164 CA GLU A 274 -5.866 -17.532 -15.400 1.00 94.50 C +ATOM 2165 C GLU A 274 -6.808 -18.425 -14.582 1.00 94.50 C +ATOM 2166 CB GLU A 274 -5.328 -18.302 -16.610 1.00 94.50 C +ATOM 2167 O GLU A 274 -6.381 -19.370 -13.919 1.00 94.50 O +ATOM 2168 CG GLU A 274 -4.635 -17.384 -17.629 1.00 94.50 C +ATOM 2169 CD GLU A 274 -4.001 -18.156 -18.797 1.00 94.50 C +ATOM 2170 OE1 GLU A 274 -3.300 -17.502 -19.600 1.00 94.50 O +ATOM 2171 OE2 GLU A 274 -4.174 -19.395 -18.863 1.00 94.50 O +ATOM 2172 N GLY A 275 -8.105 -18.109 -14.607 1.00 91.38 N +ATOM 2173 CA GLY A 275 -9.130 -18.845 -13.859 1.00 91.38 C +ATOM 2174 C GLY A 275 -9.109 -18.635 -12.339 1.00 91.38 C +ATOM 2175 O GLY A 275 -9.959 -19.192 -11.647 1.00 91.38 O +ATOM 2176 N VAL A 276 -8.185 -17.828 -11.807 1.00 91.00 N +ATOM 2177 CA VAL A 276 -8.175 -17.433 -10.392 1.00 91.00 C +ATOM 2178 C VAL A 276 -9.214 -16.334 -10.170 1.00 91.00 C +ATOM 2179 CB VAL A 276 -6.769 -16.984 -9.939 1.00 91.00 C +ATOM 2180 O VAL A 276 -9.309 -15.396 -10.960 1.00 91.00 O +ATOM 2181 CG1 VAL A 276 -6.743 -16.573 -8.459 1.00 91.00 C +ATOM 2182 CG2 VAL A 276 -5.746 -18.114 -10.121 1.00 91.00 C +ATOM 2183 N SER A 277 -9.972 -16.417 -9.074 1.00 93.25 N +ATOM 2184 CA SER A 277 -10.877 -15.346 -8.638 1.00 93.25 C +ATOM 2185 C SER A 277 -10.070 -14.175 -8.066 1.00 93.25 C +ATOM 2186 CB SER A 277 -11.880 -15.898 -7.618 1.00 93.25 C +ATOM 2187 O SER A 277 -9.901 -14.070 -6.853 1.00 93.25 O +ATOM 2188 OG SER A 277 -12.807 -14.900 -7.249 1.00 93.25 O +ATOM 2189 N PHE A 278 -9.520 -13.323 -8.929 1.00 96.56 N +ATOM 2190 CA PHE A 278 -8.732 -12.159 -8.529 1.00 96.56 C +ATOM 2191 C PHE A 278 -9.607 -10.892 -8.560 1.00 96.56 C +ATOM 2192 CB PHE A 278 -7.500 -12.048 -9.435 1.00 96.56 C +ATOM 2193 O PHE A 278 -10.375 -10.707 -9.504 1.00 96.56 O +ATOM 2194 CG PHE A 278 -6.391 -11.166 -8.910 1.00 96.56 C +ATOM 2195 CD1 PHE A 278 -6.284 -9.831 -9.342 1.00 96.56 C +ATOM 2196 CD2 PHE A 278 -5.449 -11.687 -8.005 1.00 96.56 C +ATOM 2197 CE1 PHE A 278 -5.237 -9.022 -8.870 1.00 96.56 C +ATOM 2198 CE2 PHE A 278 -4.404 -10.875 -7.531 1.00 96.56 C +ATOM 2199 CZ PHE A 278 -4.302 -9.541 -7.957 1.00 96.56 C +ATOM 2200 N PRO A 279 -9.520 -10.003 -7.555 1.00 97.12 N +ATOM 2201 CA PRO A 279 -10.315 -8.778 -7.534 1.00 97.12 C +ATOM 2202 C PRO A 279 -9.957 -7.852 -8.702 1.00 97.12 C +ATOM 2203 CB PRO A 279 -10.003 -8.121 -6.189 1.00 97.12 C +ATOM 2204 O PRO A 279 -8.779 -7.709 -9.067 1.00 97.12 O +ATOM 2205 CG PRO A 279 -8.605 -8.625 -5.885 1.00 97.12 C +ATOM 2206 CD PRO A 279 -8.647 -10.057 -6.394 1.00 97.12 C +ATOM 2207 N LYS A 280 -10.960 -7.139 -9.231 1.00 97.62 N +ATOM 2208 CA LYS A 280 -10.724 -6.073 -10.212 1.00 97.62 C +ATOM 2209 C LYS A 280 -9.691 -5.086 -9.675 1.00 97.62 C +ATOM 2210 CB LYS A 280 -12.031 -5.380 -10.638 1.00 97.62 C +ATOM 2211 O LYS A 280 -9.637 -4.801 -8.478 1.00 97.62 O +ATOM 2212 CG LYS A 280 -12.725 -4.637 -9.489 1.00 97.62 C +ATOM 2213 CD LYS A 280 -13.938 -3.838 -9.969 1.00 97.62 C +ATOM 2214 CE LYS A 280 -14.431 -2.959 -8.816 1.00 97.62 C +ATOM 2215 NZ LYS A 280 -15.634 -2.193 -9.203 1.00 97.62 N +ATOM 2216 N SER A 281 -8.832 -4.579 -10.544 1.00 98.81 N +ATOM 2217 CA SER A 281 -7.655 -3.821 -10.120 1.00 98.81 C +ATOM 2218 C SER A 281 -7.588 -2.453 -10.790 1.00 98.81 C +ATOM 2219 CB SER A 281 -6.396 -4.646 -10.396 1.00 98.81 C +ATOM 2220 O SER A 281 -7.475 -2.362 -12.009 1.00 98.81 O +ATOM 2221 OG SER A 281 -6.352 -5.795 -9.561 1.00 98.81 O +ATOM 2222 N LEU A 282 -7.585 -1.386 -9.992 1.00 98.88 N +ATOM 2223 CA LEU A 282 -7.156 -0.056 -10.412 1.00 98.88 C +ATOM 2224 C LEU A 282 -5.635 0.044 -10.244 1.00 98.88 C +ATOM 2225 CB LEU A 282 -7.916 1.009 -9.600 1.00 98.88 C +ATOM 2226 O LEU A 282 -5.125 0.048 -9.124 1.00 98.88 O +ATOM 2227 CG LEU A 282 -7.451 2.456 -9.857 1.00 98.88 C +ATOM 2228 CD1 LEU A 282 -7.630 2.889 -11.315 1.00 98.88 C +ATOM 2229 CD2 LEU A 282 -8.238 3.413 -8.965 1.00 98.88 C +ATOM 2230 N VAL A 283 -4.906 0.137 -11.356 1.00 98.88 N +ATOM 2231 CA VAL A 283 -3.445 0.298 -11.366 1.00 98.88 C +ATOM 2232 C VAL A 283 -3.109 1.721 -11.785 1.00 98.88 C +ATOM 2233 CB VAL A 283 -2.753 -0.742 -12.270 1.00 98.88 C +ATOM 2234 O VAL A 283 -3.353 2.114 -12.925 1.00 98.88 O +ATOM 2235 CG1 VAL A 283 -1.226 -0.637 -12.149 1.00 98.88 C +ATOM 2236 CG2 VAL A 283 -3.147 -2.171 -11.879 1.00 98.88 C +ATOM 2237 N VAL A 284 -2.539 2.500 -10.868 1.00 98.81 N +ATOM 2238 CA VAL A 284 -2.117 3.882 -11.117 1.00 98.81 C +ATOM 2239 C VAL A 284 -0.618 3.905 -11.386 1.00 98.81 C +ATOM 2240 CB VAL A 284 -2.501 4.805 -9.951 1.00 98.81 C +ATOM 2241 O VAL A 284 0.173 3.427 -10.574 1.00 98.81 O +ATOM 2242 CG1 VAL A 284 -2.102 6.257 -10.229 1.00 98.81 C +ATOM 2243 CG2 VAL A 284 -4.010 4.770 -9.675 1.00 98.81 C +ATOM 2244 N VAL A 285 -0.230 4.470 -12.526 1.00 98.69 N +ATOM 2245 CA VAL A 285 1.154 4.494 -13.014 1.00 98.69 C +ATOM 2246 C VAL A 285 1.570 5.934 -13.270 1.00 98.69 C +ATOM 2247 CB VAL A 285 1.288 3.633 -14.285 1.00 98.69 C +ATOM 2248 O VAL A 285 0.946 6.633 -14.071 1.00 98.69 O +ATOM 2249 CG1 VAL A 285 2.692 3.691 -14.889 1.00 98.69 C +ATOM 2250 CG2 VAL A 285 0.943 2.175 -13.971 1.00 98.69 C +ATOM 2251 N ALA A 286 2.646 6.379 -12.623 1.00 96.94 N +ATOM 2252 CA ALA A 286 3.247 7.675 -12.919 1.00 96.94 C +ATOM 2253 C ALA A 286 4.244 7.533 -14.079 1.00 96.94 C +ATOM 2254 CB ALA A 286 3.866 8.252 -11.643 1.00 96.94 C +ATOM 2255 O ALA A 286 5.165 6.721 -14.024 1.00 96.94 O +ATOM 2256 N GLY A 287 4.078 8.330 -15.134 1.00 96.56 N +ATOM 2257 CA GLY A 287 4.875 8.215 -16.358 1.00 96.56 C +ATOM 2258 C GLY A 287 6.360 8.554 -16.194 1.00 96.56 C +ATOM 2259 O GLY A 287 7.174 8.178 -17.040 1.00 96.56 O +ATOM 2260 N LEU A 288 6.716 9.219 -15.093 1.00 95.56 N +ATOM 2261 CA LEU A 288 8.085 9.568 -14.712 1.00 95.56 C +ATOM 2262 C LEU A 288 8.594 8.732 -13.521 1.00 95.56 C +ATOM 2263 CB LEU A 288 8.154 11.083 -14.463 1.00 95.56 C +ATOM 2264 O LEU A 288 9.542 9.128 -12.841 1.00 95.56 O +ATOM 2265 CG LEU A 288 7.662 11.957 -15.633 1.00 95.56 C +ATOM 2266 CD1 LEU A 288 7.614 13.418 -15.205 1.00 95.56 C +ATOM 2267 CD2 LEU A 288 8.568 11.837 -16.858 1.00 95.56 C +ATOM 2268 N ASP A 289 7.960 7.588 -13.246 1.00 96.19 N +ATOM 2269 CA ASP A 289 8.463 6.578 -12.313 1.00 96.19 C +ATOM 2270 C ASP A 289 9.571 5.738 -12.967 1.00 96.19 C +ATOM 2271 CB ASP A 289 7.302 5.701 -11.813 1.00 96.19 C +ATOM 2272 O ASP A 289 9.437 5.263 -14.095 1.00 96.19 O +ATOM 2273 CG ASP A 289 7.621 4.910 -10.542 1.00 96.19 C +ATOM 2274 OD1 ASP A 289 8.816 4.619 -10.298 1.00 96.19 O +ATOM 2275 OD2 ASP A 289 6.660 4.655 -9.774 1.00 96.19 O +ATOM 2276 N LEU A 290 10.662 5.510 -12.235 1.00 96.75 N +ATOM 2277 CA LEU A 290 11.785 4.679 -12.672 1.00 96.75 C +ATOM 2278 C LEU A 290 11.338 3.254 -13.028 1.00 96.75 C +ATOM 2279 CB LEU A 290 12.835 4.716 -11.547 1.00 96.75 C +ATOM 2280 O LEU A 290 11.846 2.669 -13.983 1.00 96.75 O +ATOM 2281 CG LEU A 290 14.120 3.920 -11.846 1.00 96.75 C +ATOM 2282 CD1 LEU A 290 15.306 4.616 -11.190 1.00 96.75 C +ATOM 2283 CD2 LEU A 290 14.076 2.504 -11.267 1.00 96.75 C +ATOM 2284 N ILE A 291 10.362 2.712 -12.292 1.00 97.25 N +ATOM 2285 CA ILE A 291 9.821 1.359 -12.504 1.00 97.25 C +ATOM 2286 C ILE A 291 8.505 1.353 -13.302 1.00 97.25 C +ATOM 2287 CB ILE A 291 9.709 0.589 -11.170 1.00 97.25 C +ATOM 2288 O ILE A 291 7.783 0.356 -13.279 1.00 97.25 O +ATOM 2289 CG1 ILE A 291 8.811 1.345 -10.173 1.00 97.25 C +ATOM 2290 CG2 ILE A 291 11.099 0.329 -10.563 1.00 97.25 C +ATOM 2291 CD1 ILE A 291 8.269 0.445 -9.067 1.00 97.25 C +ATOM 2292 N ARG A 292 8.183 2.445 -14.012 1.00 98.00 N +ATOM 2293 CA ARG A 292 6.959 2.576 -14.822 1.00 98.00 C +ATOM 2294 C ARG A 292 6.745 1.384 -15.748 1.00 98.00 C +ATOM 2295 CB ARG A 292 7.046 3.861 -15.655 1.00 98.00 C +ATOM 2296 O ARG A 292 5.648 0.838 -15.790 1.00 98.00 O +ATOM 2297 CG ARG A 292 5.845 4.053 -16.602 1.00 98.00 C +ATOM 2298 CD ARG A 292 6.172 5.043 -17.708 1.00 98.00 C +ATOM 2299 NE ARG A 292 7.211 4.532 -18.616 1.00 98.00 N +ATOM 2300 NH1 ARG A 292 8.059 6.560 -19.302 1.00 98.00 N +ATOM 2301 NH2 ARG A 292 8.884 4.619 -20.091 1.00 98.00 N +ATOM 2302 CZ ARG A 292 8.040 5.253 -19.334 1.00 98.00 C +ATOM 2303 N ASP A 293 7.770 0.972 -16.488 1.00 98.75 N +ATOM 2304 CA ASP A 293 7.613 -0.089 -17.486 1.00 98.75 C +ATOM 2305 C ASP A 293 7.258 -1.436 -16.826 1.00 98.75 C +ATOM 2306 CB ASP A 293 8.884 -0.181 -18.336 1.00 98.75 C +ATOM 2307 O ASP A 293 6.437 -2.180 -17.358 1.00 98.75 O +ATOM 2308 CG ASP A 293 9.152 1.037 -19.240 1.00 98.75 C +ATOM 2309 OD1 ASP A 293 8.295 1.953 -19.349 1.00 98.75 O +ATOM 2310 OD2 ASP A 293 10.260 1.095 -19.819 1.00 98.75 O +ATOM 2311 N TRP A 294 7.762 -1.706 -15.615 1.00 98.56 N +ATOM 2312 CA TRP A 294 7.362 -2.873 -14.819 1.00 98.56 C +ATOM 2313 C TRP A 294 5.926 -2.778 -14.291 1.00 98.56 C +ATOM 2314 CB TRP A 294 8.355 -3.086 -13.669 1.00 98.56 C +ATOM 2315 O TRP A 294 5.224 -3.785 -14.226 1.00 98.56 O +ATOM 2316 CG TRP A 294 9.675 -3.630 -14.107 1.00 98.56 C +ATOM 2317 CD1 TRP A 294 10.879 -3.020 -14.014 1.00 98.56 C +ATOM 2318 CD2 TRP A 294 9.920 -4.909 -14.763 1.00 98.56 C +ATOM 2319 CE2 TRP A 294 11.313 -5.012 -15.039 1.00 98.56 C +ATOM 2320 CE3 TRP A 294 9.095 -5.986 -15.158 1.00 98.56 C +ATOM 2321 NE1 TRP A 294 11.855 -3.839 -14.553 1.00 98.56 N +ATOM 2322 CH2 TRP A 294 11.008 -7.178 -16.089 1.00 98.56 C +ATOM 2323 CZ2 TRP A 294 11.851 -6.130 -15.687 1.00 98.56 C +ATOM 2324 CZ3 TRP A 294 9.633 -7.108 -15.815 1.00 98.56 C +ATOM 2325 N GLN A 295 5.451 -1.577 -13.954 1.00 98.69 N +ATOM 2326 CA GLN A 295 4.053 -1.350 -13.567 1.00 98.69 C +ATOM 2327 C GLN A 295 3.096 -1.562 -14.751 1.00 98.69 C +ATOM 2328 CB GLN A 295 3.888 0.075 -13.021 1.00 98.69 C +ATOM 2329 O GLN A 295 2.040 -2.179 -14.597 1.00 98.69 O +ATOM 2330 CG GLN A 295 4.664 0.330 -11.723 1.00 98.69 C +ATOM 2331 CD GLN A 295 4.868 1.806 -11.386 1.00 98.69 C +ATOM 2332 NE2 GLN A 295 5.419 2.092 -10.230 1.00 98.69 N +ATOM 2333 OE1 GLN A 295 4.540 2.719 -12.125 1.00 98.69 O +ATOM 2334 N LEU A 296 3.476 -1.089 -15.943 1.00 98.75 N +ATOM 2335 CA LEU A 296 2.730 -1.318 -17.182 1.00 98.75 C +ATOM 2336 C LEU A 296 2.728 -2.804 -17.563 1.00 98.75 C +ATOM 2337 CB LEU A 296 3.327 -0.462 -18.314 1.00 98.75 C +ATOM 2338 O LEU A 296 1.675 -3.344 -17.898 1.00 98.75 O +ATOM 2339 CG LEU A 296 3.165 1.059 -18.139 1.00 98.75 C +ATOM 2340 CD1 LEU A 296 3.909 1.792 -19.257 1.00 98.75 C +ATOM 2341 CD2 LEU A 296 1.701 1.498 -18.181 1.00 98.75 C +ATOM 2342 N ALA A 297 3.875 -3.481 -17.448 1.00 98.44 N +ATOM 2343 CA ALA A 297 3.982 -4.921 -17.670 1.00 98.44 C +ATOM 2344 C ALA A 297 3.099 -5.721 -16.699 1.00 98.44 C +ATOM 2345 CB ALA A 297 5.453 -5.334 -17.550 1.00 98.44 C +ATOM 2346 O ALA A 297 2.454 -6.679 -17.115 1.00 98.44 O +ATOM 2347 N TYR A 298 3.005 -5.300 -15.434 1.00 98.62 N +ATOM 2348 CA TYR A 298 2.090 -5.899 -14.463 1.00 98.62 C +ATOM 2349 C TYR A 298 0.616 -5.748 -14.879 1.00 98.62 C +ATOM 2350 CB TYR A 298 2.353 -5.288 -13.082 1.00 98.62 C +ATOM 2351 O TYR A 298 -0.131 -6.726 -14.861 1.00 98.62 O +ATOM 2352 CG TYR A 298 1.328 -5.699 -12.051 1.00 98.62 C +ATOM 2353 CD1 TYR A 298 0.189 -4.901 -11.833 1.00 98.62 C +ATOM 2354 CD2 TYR A 298 1.489 -6.907 -11.352 1.00 98.62 C +ATOM 2355 CE1 TYR A 298 -0.800 -5.322 -10.928 1.00 98.62 C +ATOM 2356 CE2 TYR A 298 0.504 -7.328 -10.443 1.00 98.62 C +ATOM 2357 OH TYR A 298 -1.621 -6.937 -9.368 1.00 98.62 O +ATOM 2358 CZ TYR A 298 -0.647 -6.538 -10.230 1.00 98.62 C +ATOM 2359 N ALA A 299 0.191 -4.548 -15.292 1.00 98.69 N +ATOM 2360 CA ALA A 299 -1.180 -4.328 -15.759 1.00 98.69 C +ATOM 2361 C ALA A 299 -1.503 -5.166 -17.012 1.00 98.69 C +ATOM 2362 CB ALA A 299 -1.383 -2.829 -16.007 1.00 98.69 C +ATOM 2363 O ALA A 299 -2.577 -5.760 -17.103 1.00 98.69 O +ATOM 2364 N GLU A 300 -0.559 -5.272 -17.949 1.00 98.38 N +ATOM 2365 CA GLU A 300 -0.698 -6.134 -19.125 1.00 98.38 C +ATOM 2366 C GLU A 300 -0.724 -7.625 -18.748 1.00 98.38 C +ATOM 2367 CB GLU A 300 0.437 -5.811 -20.109 1.00 98.38 C +ATOM 2368 O GLU A 300 -1.476 -8.395 -19.341 1.00 98.38 O +ATOM 2369 CG GLU A 300 0.364 -6.586 -21.434 1.00 98.38 C +ATOM 2370 CD GLU A 300 -0.915 -6.350 -22.261 1.00 98.38 C +ATOM 2371 OE1 GLU A 300 -1.167 -7.200 -23.145 1.00 98.38 O +ATOM 2372 OE2 GLU A 300 -1.620 -5.332 -22.061 1.00 98.38 O +ATOM 2373 N GLY A 301 0.044 -8.039 -17.736 1.00 98.06 N +ATOM 2374 CA GLY A 301 0.012 -9.397 -17.190 1.00 98.06 C +ATOM 2375 C GLY A 301 -1.358 -9.769 -16.619 1.00 98.06 C +ATOM 2376 O GLY A 301 -1.890 -10.824 -16.961 1.00 98.06 O +ATOM 2377 N LEU A 302 -1.979 -8.874 -15.836 1.00 98.44 N +ATOM 2378 CA LEU A 302 -3.354 -9.059 -15.347 1.00 98.44 C +ATOM 2379 C LEU A 302 -4.341 -9.224 -16.508 1.00 98.44 C +ATOM 2380 CB LEU A 302 -3.786 -7.861 -14.477 1.00 98.44 C +ATOM 2381 O LEU A 302 -5.133 -10.166 -16.521 1.00 98.44 O +ATOM 2382 CG LEU A 302 -3.171 -7.781 -13.072 1.00 98.44 C +ATOM 2383 CD1 LEU A 302 -3.648 -6.485 -12.412 1.00 98.44 C +ATOM 2384 CD2 LEU A 302 -3.618 -8.935 -12.175 1.00 98.44 C +ATOM 2385 N LYS A 303 -4.251 -8.345 -17.512 1.00 98.12 N +ATOM 2386 CA LYS A 303 -5.118 -8.387 -18.694 1.00 98.12 C +ATOM 2387 C LYS A 303 -4.964 -9.693 -19.478 1.00 98.12 C +ATOM 2388 CB LYS A 303 -4.800 -7.170 -19.565 1.00 98.12 C +ATOM 2389 O LYS A 303 -5.965 -10.290 -19.865 1.00 98.12 O +ATOM 2390 CG LYS A 303 -5.742 -7.062 -20.768 1.00 98.12 C +ATOM 2391 CD LYS A 303 -5.316 -5.859 -21.602 1.00 98.12 C +ATOM 2392 CE LYS A 303 -6.159 -5.766 -22.870 1.00 98.12 C +ATOM 2393 NZ LYS A 303 -5.594 -4.718 -23.746 1.00 98.12 N +ATOM 2394 N LYS A 304 -3.728 -10.150 -19.705 1.00 97.56 N +ATOM 2395 CA LYS A 304 -3.441 -11.418 -20.400 1.00 97.56 C +ATOM 2396 C LYS A 304 -3.979 -12.627 -19.644 1.00 97.56 C +ATOM 2397 CB LYS A 304 -1.934 -11.571 -20.620 1.00 97.56 C +ATOM 2398 O LYS A 304 -4.468 -13.548 -20.281 1.00 97.56 O +ATOM 2399 CG LYS A 304 -1.456 -10.699 -21.784 1.00 97.56 C +ATOM 2400 CD LYS A 304 0.062 -10.807 -21.915 1.00 97.56 C +ATOM 2401 CE LYS A 304 0.517 -9.967 -23.106 1.00 97.56 C +ATOM 2402 NZ LYS A 304 1.994 -9.907 -23.177 1.00 97.56 N +ATOM 2403 N ALA A 305 -3.956 -12.584 -18.314 1.00 96.94 N +ATOM 2404 CA ALA A 305 -4.539 -13.619 -17.468 1.00 96.94 C +ATOM 2405 C ALA A 305 -6.081 -13.562 -17.376 1.00 96.94 C +ATOM 2406 CB ALA A 305 -3.869 -13.535 -16.096 1.00 96.94 C +ATOM 2407 O ALA A 305 -6.676 -14.309 -16.601 1.00 96.94 O +ATOM 2408 N GLY A 306 -6.736 -12.673 -18.133 1.00 97.56 N +ATOM 2409 CA GLY A 306 -8.193 -12.528 -18.156 1.00 97.56 C +ATOM 2410 C GLY A 306 -8.779 -11.783 -16.955 1.00 97.56 C +ATOM 2411 O GLY A 306 -9.986 -11.854 -16.742 1.00 97.56 O +ATOM 2412 N GLN A 307 -7.952 -11.080 -16.175 1.00 97.88 N +ATOM 2413 CA GLN A 307 -8.393 -10.339 -14.993 1.00 97.88 C +ATOM 2414 C GLN A 307 -8.855 -8.922 -15.347 1.00 97.88 C +ATOM 2415 CB GLN A 307 -7.285 -10.327 -13.922 1.00 97.88 C +ATOM 2416 O GLN A 307 -8.332 -8.284 -16.267 1.00 97.88 O +ATOM 2417 CG GLN A 307 -6.847 -11.742 -13.502 1.00 97.88 C +ATOM 2418 CD GLN A 307 -7.997 -12.566 -12.929 1.00 97.88 C +ATOM 2419 NE2 GLN A 307 -7.920 -13.870 -12.920 1.00 97.88 N +ATOM 2420 OE1 GLN A 307 -8.984 -12.050 -12.439 1.00 97.88 O +ATOM 2421 N GLU A 308 -9.821 -8.399 -14.590 1.00 98.06 N +ATOM 2422 CA GLU A 308 -10.324 -7.040 -14.786 1.00 98.06 C +ATOM 2423 C GLU A 308 -9.312 -6.010 -14.270 1.00 98.06 C +ATOM 2424 CB GLU A 308 -11.703 -6.874 -14.137 1.00 98.06 C +ATOM 2425 O GLU A 308 -9.007 -5.935 -13.077 1.00 98.06 O +ATOM 2426 CG GLU A 308 -12.326 -5.520 -14.517 1.00 98.06 C +ATOM 2427 CD GLU A 308 -13.691 -5.280 -13.859 1.00 98.06 C +ATOM 2428 OE1 GLU A 308 -13.979 -4.093 -13.576 1.00 98.06 O +ATOM 2429 OE2 GLU A 308 -14.424 -6.266 -13.643 1.00 98.06 O +ATOM 2430 N VAL A 309 -8.789 -5.184 -15.176 1.00 98.62 N +ATOM 2431 CA VAL A 309 -7.804 -4.153 -14.847 1.00 98.62 C +ATOM 2432 C VAL A 309 -8.173 -2.818 -15.481 1.00 98.62 C +ATOM 2433 CB VAL A 309 -6.373 -4.624 -15.175 1.00 98.62 C +ATOM 2434 O VAL A 309 -8.324 -2.698 -16.698 1.00 98.62 O +ATOM 2435 CG1 VAL A 309 -6.136 -4.950 -16.654 1.00 98.62 C +ATOM 2436 CG2 VAL A 309 -5.330 -3.600 -14.714 1.00 98.62 C +ATOM 2437 N LYS A 310 -8.269 -1.784 -14.644 1.00 98.56 N +ATOM 2438 CA LYS A 310 -8.337 -0.386 -15.061 1.00 98.56 C +ATOM 2439 C LYS A 310 -6.963 0.245 -14.866 1.00 98.56 C +ATOM 2440 CB LYS A 310 -9.460 0.332 -14.298 1.00 98.56 C +ATOM 2441 O LYS A 310 -6.499 0.419 -13.743 1.00 98.56 O +ATOM 2442 CG LYS A 310 -9.674 1.749 -14.853 1.00 98.56 C +ATOM 2443 CD LYS A 310 -10.932 2.404 -14.270 1.00 98.56 C +ATOM 2444 CE LYS A 310 -11.115 3.799 -14.880 1.00 98.56 C +ATOM 2445 NZ LYS A 310 -12.321 4.475 -14.353 1.00 98.56 N +ATOM 2446 N LEU A 311 -6.302 0.584 -15.969 1.00 98.69 N +ATOM 2447 CA LEU A 311 -4.999 1.246 -15.955 1.00 98.69 C +ATOM 2448 C LEU A 311 -5.174 2.766 -16.037 1.00 98.69 C +ATOM 2449 CB LEU A 311 -4.141 0.687 -17.103 1.00 98.69 C +ATOM 2450 O LEU A 311 -5.712 3.280 -17.016 1.00 98.69 O +ATOM 2451 CG LEU A 311 -2.771 1.373 -17.264 1.00 98.69 C +ATOM 2452 CD1 LEU A 311 -1.866 1.183 -16.048 1.00 98.69 C +ATOM 2453 CD2 LEU A 311 -2.057 0.804 -18.486 1.00 98.69 C +ATOM 2454 N MET A 312 -4.654 3.485 -15.045 1.00 98.44 N +ATOM 2455 CA MET A 312 -4.547 4.941 -15.052 1.00 98.44 C +ATOM 2456 C MET A 312 -3.078 5.344 -15.205 1.00 98.44 C +ATOM 2457 CB MET A 312 -5.208 5.513 -13.793 1.00 98.44 C +ATOM 2458 O MET A 312 -2.350 5.498 -14.225 1.00 98.44 O +ATOM 2459 CG MET A 312 -5.371 7.031 -13.899 1.00 98.44 C +ATOM 2460 SD MET A 312 -6.260 7.726 -12.488 1.00 98.44 S +ATOM 2461 CE MET A 312 -6.875 9.268 -13.203 1.00 98.44 C +ATOM 2462 N HIS A 313 -2.634 5.500 -16.453 1.00 98.19 N +ATOM 2463 CA HIS A 313 -1.277 5.943 -16.770 1.00 98.19 C +ATOM 2464 C HIS A 313 -1.224 7.469 -16.926 1.00 98.19 C +ATOM 2465 CB HIS A 313 -0.764 5.200 -18.009 1.00 98.19 C +ATOM 2466 O HIS A 313 -1.841 8.042 -17.822 1.00 98.19 O +ATOM 2467 CG HIS A 313 0.645 5.570 -18.414 1.00 98.19 C +ATOM 2468 CD2 HIS A 313 1.666 5.995 -17.603 1.00 98.19 C +ATOM 2469 ND1 HIS A 313 1.141 5.545 -19.696 1.00 98.19 N +ATOM 2470 CE1 HIS A 313 2.419 5.951 -19.661 1.00 98.19 C +ATOM 2471 NE2 HIS A 313 2.785 6.242 -18.402 1.00 98.19 N +ATOM 2472 N LEU A 314 -0.479 8.132 -16.044 1.00 97.56 N +ATOM 2473 CA LEU A 314 -0.350 9.585 -15.988 1.00 97.56 C +ATOM 2474 C LEU A 314 1.043 9.991 -16.468 1.00 97.56 C +ATOM 2475 CB LEU A 314 -0.643 10.050 -14.551 1.00 97.56 C +ATOM 2476 O LEU A 314 1.963 10.131 -15.665 1.00 97.56 O +ATOM 2477 CG LEU A 314 -2.066 9.719 -14.074 1.00 97.56 C +ATOM 2478 CD1 LEU A 314 -2.166 9.927 -12.573 1.00 97.56 C +ATOM 2479 CD2 LEU A 314 -3.121 10.596 -14.752 1.00 97.56 C +ATOM 2480 N GLU A 315 1.194 10.193 -17.778 1.00 94.38 N +ATOM 2481 CA GLU A 315 2.490 10.363 -18.465 1.00 94.38 C +ATOM 2482 C GLU A 315 3.422 11.416 -17.839 1.00 94.38 C +ATOM 2483 CB GLU A 315 2.242 10.774 -19.922 1.00 94.38 C +ATOM 2484 O GLU A 315 4.639 11.251 -17.840 1.00 94.38 O +ATOM 2485 CG GLU A 315 1.570 9.672 -20.752 1.00 94.38 C +ATOM 2486 CD GLU A 315 1.369 10.086 -22.218 1.00 94.38 C +ATOM 2487 OE1 GLU A 315 0.961 9.201 -23.000 1.00 94.38 O +ATOM 2488 OE2 GLU A 315 1.604 11.277 -22.537 1.00 94.38 O +ATOM 2489 N LYS A 316 2.855 12.495 -17.285 1.00 92.62 N +ATOM 2490 CA LYS A 316 3.602 13.620 -16.694 1.00 92.62 C +ATOM 2491 C LYS A 316 3.696 13.576 -15.165 1.00 92.62 C +ATOM 2492 CB LYS A 316 3.010 14.951 -17.183 1.00 92.62 C +ATOM 2493 O LYS A 316 4.306 14.461 -14.571 1.00 92.62 O +ATOM 2494 CG LYS A 316 3.123 15.106 -18.706 1.00 92.62 C +ATOM 2495 CD LYS A 316 2.616 16.480 -19.152 1.00 92.62 C +ATOM 2496 CE LYS A 316 2.752 16.589 -20.673 1.00 92.62 C +ATOM 2497 NZ LYS A 316 2.283 17.903 -21.175 1.00 92.62 N +ATOM 2498 N ALA A 317 3.101 12.576 -14.519 1.00 94.06 N +ATOM 2499 CA ALA A 317 3.159 12.439 -13.071 1.00 94.06 C +ATOM 2500 C ALA A 317 4.516 11.876 -12.627 1.00 94.06 C +ATOM 2501 CB ALA A 317 1.988 11.581 -12.587 1.00 94.06 C +ATOM 2502 O ALA A 317 5.074 10.986 -13.271 1.00 94.06 O +ATOM 2503 N THR A 318 5.017 12.362 -11.491 1.00 92.25 N +ATOM 2504 CA THR A 318 6.223 11.857 -10.816 1.00 92.25 C +ATOM 2505 C THR A 318 5.870 10.993 -9.614 1.00 92.25 C +ATOM 2506 CB THR A 318 7.127 13.012 -10.372 1.00 92.25 C +ATOM 2507 O THR A 318 4.757 11.061 -9.103 1.00 92.25 O +ATOM 2508 CG2 THR A 318 7.622 13.791 -11.588 1.00 92.25 C +ATOM 2509 OG1 THR A 318 6.425 13.895 -9.522 1.00 92.25 O +ATOM 2510 N VAL A 319 6.813 10.180 -9.134 1.00 90.31 N +ATOM 2511 CA VAL A 319 6.617 9.343 -7.936 1.00 90.31 C +ATOM 2512 C VAL A 319 6.104 10.182 -6.759 1.00 90.31 C +ATOM 2513 CB VAL A 319 7.924 8.625 -7.543 1.00 90.31 C +ATOM 2514 O VAL A 319 6.616 11.267 -6.495 1.00 90.31 O +ATOM 2515 CG1 VAL A 319 7.711 7.657 -6.370 1.00 90.31 C +ATOM 2516 CG2 VAL A 319 8.475 7.816 -8.722 1.00 90.31 C +ATOM 2517 N GLY A 320 5.091 9.681 -6.046 1.00 88.12 N +ATOM 2518 CA GLY A 320 4.547 10.329 -4.849 1.00 88.12 C +ATOM 2519 C GLY A 320 3.619 11.522 -5.108 1.00 88.12 C +ATOM 2520 O GLY A 320 3.239 12.201 -4.153 1.00 88.12 O +ATOM 2521 N PHE A 321 3.208 11.780 -6.358 1.00 93.50 N +ATOM 2522 CA PHE A 321 2.285 12.875 -6.697 1.00 93.50 C +ATOM 2523 C PHE A 321 0.958 12.842 -5.913 1.00 93.50 C +ATOM 2524 CB PHE A 321 1.994 12.862 -8.205 1.00 93.50 C +ATOM 2525 O PHE A 321 0.360 13.893 -5.702 1.00 93.50 O +ATOM 2526 CG PHE A 321 1.085 11.732 -8.655 1.00 93.50 C +ATOM 2527 CD1 PHE A 321 1.609 10.470 -8.995 1.00 93.50 C +ATOM 2528 CD2 PHE A 321 -0.306 11.931 -8.692 1.00 93.50 C +ATOM 2529 CE1 PHE A 321 0.771 9.435 -9.422 1.00 93.50 C +ATOM 2530 CE2 PHE A 321 -1.153 10.884 -9.089 1.00 93.50 C +ATOM 2531 CZ PHE A 321 -0.607 9.646 -9.471 1.00 93.50 C +ATOM 2532 N TYR A 322 0.517 11.660 -5.465 1.00 93.94 N +ATOM 2533 CA TYR A 322 -0.705 11.436 -4.681 1.00 93.94 C +ATOM 2534 C TYR A 322 -0.544 11.706 -3.169 1.00 93.94 C +ATOM 2535 CB TYR A 322 -1.225 10.016 -4.947 1.00 93.94 C +ATOM 2536 O TYR A 322 -1.528 11.676 -2.428 1.00 93.94 O +ATOM 2537 CG TYR A 322 -0.170 8.934 -4.816 1.00 93.94 C +ATOM 2538 CD1 TYR A 322 0.427 8.413 -5.978 1.00 93.94 C +ATOM 2539 CD2 TYR A 322 0.239 8.474 -3.549 1.00 93.94 C +ATOM 2540 CE1 TYR A 322 1.455 7.460 -5.876 1.00 93.94 C +ATOM 2541 CE2 TYR A 322 1.264 7.511 -3.444 1.00 93.94 C +ATOM 2542 OH TYR A 322 2.917 6.135 -4.518 1.00 93.94 O +ATOM 2543 CZ TYR A 322 1.883 7.011 -4.610 1.00 93.94 C +ATOM 2544 N LEU A 323 0.671 11.982 -2.678 1.00 91.56 N +ATOM 2545 CA LEU A 323 0.905 12.250 -1.252 1.00 91.56 C +ATOM 2546 C LEU A 323 0.413 13.647 -0.839 1.00 91.56 C +ATOM 2547 CB LEU A 323 2.399 12.060 -0.924 1.00 91.56 C +ATOM 2548 O LEU A 323 -0.091 13.822 0.274 1.00 91.56 O +ATOM 2549 CG LEU A 323 2.934 10.627 -1.133 1.00 91.56 C +ATOM 2550 CD1 LEU A 323 4.440 10.603 -0.868 1.00 91.56 C +ATOM 2551 CD2 LEU A 323 2.266 9.615 -0.199 1.00 91.56 C +ATOM 2552 N LEU A 324 0.508 14.633 -1.740 1.00 90.94 N +ATOM 2553 CA LEU A 324 0.133 16.027 -1.486 1.00 90.94 C +ATOM 2554 C LEU A 324 -0.942 16.519 -2.475 1.00 90.94 C +ATOM 2555 CB LEU A 324 1.378 16.930 -1.557 1.00 90.94 C +ATOM 2556 O LEU A 324 -0.789 16.306 -3.676 1.00 90.94 O +ATOM 2557 CG LEU A 324 2.471 16.647 -0.508 1.00 90.94 C +ATOM 2558 CD1 LEU A 324 3.623 17.632 -0.715 1.00 90.94 C +ATOM 2559 CD2 LEU A 324 1.966 16.813 0.927 1.00 90.94 C +ATOM 2560 N PRO A 325 -1.981 17.240 -2.005 1.00 91.56 N +ATOM 2561 CA PRO A 325 -3.067 17.763 -2.836 1.00 91.56 C +ATOM 2562 C PRO A 325 -2.651 19.036 -3.587 1.00 91.56 C +ATOM 2563 CB PRO A 325 -4.227 17.996 -1.856 1.00 91.56 C +ATOM 2564 O PRO A 325 -3.161 20.119 -3.316 1.00 91.56 O +ATOM 2565 CG PRO A 325 -3.503 18.431 -0.587 1.00 91.56 C +ATOM 2566 CD PRO A 325 -2.242 17.568 -0.607 1.00 91.56 C +ATOM 2567 N ASN A 326 -1.644 18.948 -4.458 1.00 90.25 N +ATOM 2568 CA ASN A 326 -1.006 20.131 -5.051 1.00 90.25 C +ATOM 2569 C ASN A 326 -0.832 20.079 -6.576 1.00 90.25 C +ATOM 2570 CB ASN A 326 0.309 20.412 -4.304 1.00 90.25 C +ATOM 2571 O ASN A 326 -0.081 20.879 -7.131 1.00 90.25 O +ATOM 2572 CG ASN A 326 1.398 19.377 -4.531 1.00 90.25 C +ATOM 2573 ND2 ASN A 326 2.482 19.497 -3.801 1.00 90.25 N +ATOM 2574 OD1 ASN A 326 1.316 18.461 -5.336 1.00 90.25 O +ATOM 2575 N ASN A 327 -1.486 19.136 -7.255 1.00 89.62 N +ATOM 2576 CA ASN A 327 -1.446 19.008 -8.710 1.00 89.62 C +ATOM 2577 C ASN A 327 -2.709 18.301 -9.238 1.00 89.62 C +ATOM 2578 CB ASN A 327 -0.145 18.282 -9.107 1.00 89.62 C +ATOM 2579 O ASN A 327 -3.399 17.609 -8.496 1.00 89.62 O +ATOM 2580 CG ASN A 327 -0.079 16.869 -8.564 1.00 89.62 C +ATOM 2581 ND2 ASN A 327 0.659 16.604 -7.512 1.00 89.62 N +ATOM 2582 OD1 ASN A 327 -0.737 15.988 -9.076 1.00 89.62 O +ATOM 2583 N ASN A 328 -3.009 18.439 -10.532 1.00 93.25 N +ATOM 2584 CA ASN A 328 -4.204 17.819 -11.122 1.00 93.25 C +ATOM 2585 C ASN A 328 -4.131 16.284 -11.155 1.00 93.25 C +ATOM 2586 CB ASN A 328 -4.435 18.388 -12.532 1.00 93.25 C +ATOM 2587 O ASN A 328 -5.163 15.630 -11.053 1.00 93.25 O +ATOM 2588 CG ASN A 328 -4.882 19.839 -12.522 1.00 93.25 C +ATOM 2589 ND2 ASN A 328 -4.763 20.529 -13.630 1.00 93.25 N +ATOM 2590 OD1 ASN A 328 -5.318 20.385 -11.528 1.00 93.25 O +ATOM 2591 N HIS A 329 -2.932 15.692 -11.253 1.00 95.25 N +ATOM 2592 CA HIS A 329 -2.781 14.233 -11.208 1.00 95.25 C +ATOM 2593 C HIS A 329 -3.297 13.662 -9.885 1.00 95.25 C +ATOM 2594 CB HIS A 329 -1.315 13.830 -11.445 1.00 95.25 C +ATOM 2595 O HIS A 329 -3.992 12.650 -9.891 1.00 95.25 O +ATOM 2596 CG HIS A 329 -0.779 14.255 -12.782 1.00 95.25 C +ATOM 2597 CD2 HIS A 329 0.390 14.924 -13.028 1.00 95.25 C +ATOM 2598 ND1 HIS A 329 -1.370 14.005 -13.997 1.00 95.25 N +ATOM 2599 CE1 HIS A 329 -0.580 14.509 -14.955 1.00 95.25 C +ATOM 2600 NE2 HIS A 329 0.503 15.085 -14.414 1.00 95.25 N +ATOM 2601 N PHE A 330 -3.016 14.344 -8.772 1.00 96.06 N +ATOM 2602 CA PHE A 330 -3.548 14.006 -7.460 1.00 96.06 C +ATOM 2603 C PHE A 330 -5.079 13.968 -7.479 1.00 96.06 C +ATOM 2604 CB PHE A 330 -3.021 15.014 -6.428 1.00 96.06 C +ATOM 2605 O PHE A 330 -5.654 12.958 -7.090 1.00 96.06 O +ATOM 2606 CG PHE A 330 -3.701 14.917 -5.087 1.00 96.06 C +ATOM 2607 CD1 PHE A 330 -4.952 15.530 -4.882 1.00 96.06 C +ATOM 2608 CD2 PHE A 330 -3.076 14.233 -4.038 1.00 96.06 C +ATOM 2609 CE1 PHE A 330 -5.607 15.397 -3.655 1.00 96.06 C +ATOM 2610 CE2 PHE A 330 -3.714 14.121 -2.800 1.00 96.06 C +ATOM 2611 CZ PHE A 330 -4.983 14.694 -2.620 1.00 96.06 C +ATOM 2612 N HIS A 331 -5.733 15.037 -7.944 1.00 96.69 N +ATOM 2613 CA HIS A 331 -7.196 15.130 -7.904 1.00 96.69 C +ATOM 2614 C HIS A 331 -7.841 14.063 -8.786 1.00 96.69 C +ATOM 2615 CB HIS A 331 -7.649 16.546 -8.278 1.00 96.69 C +ATOM 2616 O HIS A 331 -8.695 13.328 -8.307 1.00 96.69 O +ATOM 2617 CG HIS A 331 -7.305 17.548 -7.206 1.00 96.69 C +ATOM 2618 CD2 HIS A 331 -6.347 18.524 -7.258 1.00 96.69 C +ATOM 2619 ND1 HIS A 331 -7.888 17.615 -5.963 1.00 96.69 N +ATOM 2620 CE1 HIS A 331 -7.294 18.606 -5.279 1.00 96.69 C +ATOM 2621 NE2 HIS A 331 -6.333 19.179 -6.020 1.00 96.69 N +ATOM 2622 N ASN A 332 -7.325 13.875 -10.003 1.00 97.38 N +ATOM 2623 CA ASN A 332 -7.815 12.846 -10.916 1.00 97.38 C +ATOM 2624 C ASN A 332 -7.741 11.440 -10.298 1.00 97.38 C +ATOM 2625 CB ASN A 332 -6.995 12.896 -12.217 1.00 97.38 C +ATOM 2626 O ASN A 332 -8.672 10.657 -10.448 1.00 97.38 O +ATOM 2627 CG ASN A 332 -7.165 14.165 -13.033 1.00 97.38 C +ATOM 2628 ND2 ASN A 332 -6.486 14.252 -14.153 1.00 97.38 N +ATOM 2629 OD1 ASN A 332 -7.893 15.087 -12.725 1.00 97.38 O +ATOM 2630 N VAL A 333 -6.649 11.109 -9.599 1.00 98.31 N +ATOM 2631 CA VAL A 333 -6.508 9.801 -8.937 1.00 98.31 C +ATOM 2632 C VAL A 333 -7.427 9.670 -7.734 1.00 98.31 C +ATOM 2633 CB VAL A 333 -5.050 9.543 -8.531 1.00 98.31 C +ATOM 2634 O VAL A 333 -7.991 8.604 -7.529 1.00 98.31 O +ATOM 2635 CG1 VAL A 333 -4.847 8.351 -7.587 1.00 98.31 C +ATOM 2636 CG2 VAL A 333 -4.270 9.248 -9.803 1.00 98.31 C +ATOM 2637 N MET A 334 -7.593 10.724 -6.938 1.00 98.12 N +ATOM 2638 CA MET A 334 -8.494 10.692 -5.783 1.00 98.12 C +ATOM 2639 C MET A 334 -9.958 10.524 -6.208 1.00 98.12 C +ATOM 2640 CB MET A 334 -8.297 11.960 -4.943 1.00 98.12 C +ATOM 2641 O MET A 334 -10.677 9.735 -5.593 1.00 98.12 O +ATOM 2642 CG MET A 334 -6.930 11.984 -4.242 1.00 98.12 C +ATOM 2643 SD MET A 334 -6.587 10.634 -3.076 1.00 98.12 S +ATOM 2644 CE MET A 334 -4.778 10.700 -3.065 1.00 98.12 C +ATOM 2645 N ASP A 335 -10.373 11.201 -7.280 1.00 97.81 N +ATOM 2646 CA ASP A 335 -11.709 11.054 -7.864 1.00 97.81 C +ATOM 2647 C ASP A 335 -11.910 9.640 -8.426 1.00 97.81 C +ATOM 2648 CB ASP A 335 -11.919 12.108 -8.967 1.00 97.81 C +ATOM 2649 O ASP A 335 -12.942 9.014 -8.188 1.00 97.81 O +ATOM 2650 CG ASP A 335 -11.985 13.554 -8.453 1.00 97.81 C +ATOM 2651 OD1 ASP A 335 -12.199 13.756 -7.234 1.00 97.81 O +ATOM 2652 OD2 ASP A 335 -11.829 14.465 -9.297 1.00 97.81 O +ATOM 2653 N GLU A 336 -10.897 9.095 -9.104 1.00 98.19 N +ATOM 2654 CA GLU A 336 -10.933 7.736 -9.647 1.00 98.19 C +ATOM 2655 C GLU A 336 -10.983 6.667 -8.544 1.00 98.19 C +ATOM 2656 CB GLU A 336 -9.725 7.558 -10.578 1.00 98.19 C +ATOM 2657 O GLU A 336 -11.762 5.725 -8.640 1.00 98.19 O +ATOM 2658 CG GLU A 336 -9.685 6.205 -11.287 1.00 98.19 C +ATOM 2659 CD GLU A 336 -10.942 5.927 -12.110 1.00 98.19 C +ATOM 2660 OE1 GLU A 336 -11.542 4.848 -11.916 1.00 98.19 O +ATOM 2661 OE2 GLU A 336 -11.267 6.682 -13.058 1.00 98.19 O +ATOM 2662 N ILE A 337 -10.222 6.830 -7.458 1.00 98.69 N +ATOM 2663 CA ILE A 337 -10.295 5.959 -6.274 1.00 98.69 C +ATOM 2664 C ILE A 337 -11.704 5.988 -5.672 1.00 98.69 C +ATOM 2665 CB ILE A 337 -9.231 6.392 -5.240 1.00 98.69 C +ATOM 2666 O ILE A 337 -12.262 4.934 -5.371 1.00 98.69 O +ATOM 2667 CG1 ILE A 337 -7.825 5.973 -5.720 1.00 98.69 C +ATOM 2668 CG2 ILE A 337 -9.500 5.831 -3.831 1.00 98.69 C +ATOM 2669 CD1 ILE A 337 -6.700 6.605 -4.888 1.00 98.69 C +ATOM 2670 N SER A 338 -12.285 7.182 -5.513 1.00 98.12 N +ATOM 2671 CA SER A 338 -13.640 7.339 -4.977 1.00 98.12 C +ATOM 2672 C SER A 338 -14.670 6.646 -5.874 1.00 98.12 C +ATOM 2673 CB SER A 338 -13.965 8.828 -4.811 1.00 98.12 C +ATOM 2674 O SER A 338 -15.474 5.854 -5.385 1.00 98.12 O +ATOM 2675 OG SER A 338 -15.212 9.004 -4.162 1.00 98.12 O +ATOM 2676 N ALA A 339 -14.605 6.861 -7.191 1.00 98.06 N +ATOM 2677 CA ALA A 339 -15.488 6.202 -8.151 1.00 98.06 C +ATOM 2678 C ALA A 339 -15.322 4.672 -8.140 1.00 98.06 C +ATOM 2679 CB ALA A 339 -15.212 6.785 -9.541 1.00 98.06 C +ATOM 2680 O ALA A 339 -16.314 3.948 -8.168 1.00 98.06 O +ATOM 2681 N PHE A 340 -14.084 4.178 -8.061 1.00 98.19 N +ATOM 2682 CA PHE A 340 -13.775 2.750 -8.057 1.00 98.19 C +ATOM 2683 C PHE A 340 -14.339 2.025 -6.829 1.00 98.19 C +ATOM 2684 CB PHE A 340 -12.255 2.579 -8.150 1.00 98.19 C +ATOM 2685 O PHE A 340 -14.894 0.936 -6.974 1.00 98.19 O +ATOM 2686 CG PHE A 340 -11.802 1.136 -8.201 1.00 98.19 C +ATOM 2687 CD1 PHE A 340 -11.423 0.471 -7.020 1.00 98.19 C +ATOM 2688 CD2 PHE A 340 -11.757 0.459 -9.434 1.00 98.19 C +ATOM 2689 CE1 PHE A 340 -10.963 -0.854 -7.078 1.00 98.19 C +ATOM 2690 CE2 PHE A 340 -11.304 -0.871 -9.491 1.00 98.19 C +ATOM 2691 CZ PHE A 340 -10.892 -1.516 -8.313 1.00 98.19 C +ATOM 2692 N VAL A 341 -14.220 2.629 -5.640 1.00 97.75 N +ATOM 2693 CA VAL A 341 -14.763 2.066 -4.392 1.00 97.75 C +ATOM 2694 C VAL A 341 -16.288 2.163 -4.370 1.00 97.75 C +ATOM 2695 CB VAL A 341 -14.136 2.746 -3.158 1.00 97.75 C +ATOM 2696 O VAL A 341 -16.961 1.176 -4.087 1.00 97.75 O +ATOM 2697 CG1 VAL A 341 -14.769 2.273 -1.843 1.00 97.75 C +ATOM 2698 CG2 VAL A 341 -12.638 2.428 -3.072 1.00 97.75 C +ATOM 2699 N ASN A 342 -16.843 3.327 -4.717 1.00 92.56 N +ATOM 2700 CA ASN A 342 -18.284 3.572 -4.624 1.00 92.56 C +ATOM 2701 C ASN A 342 -19.106 2.829 -5.689 1.00 92.56 C +ATOM 2702 CB ASN A 342 -18.545 5.084 -4.689 1.00 92.56 C +ATOM 2703 O ASN A 342 -20.304 2.661 -5.503 1.00 92.56 O +ATOM 2704 CG ASN A 342 -18.017 5.848 -3.486 1.00 92.56 C +ATOM 2705 ND2 ASN A 342 -17.674 7.102 -3.660 1.00 92.56 N +ATOM 2706 OD1 ASN A 342 -17.914 5.362 -2.373 1.00 92.56 O +ATOM 2707 N ALA A 343 -18.501 2.370 -6.790 1.00 89.19 N +ATOM 2708 CA ALA A 343 -19.205 1.592 -7.814 1.00 89.19 C +ATOM 2709 C ALA A 343 -19.745 0.236 -7.308 1.00 89.19 C +ATOM 2710 CB ALA A 343 -18.260 1.403 -9.007 1.00 89.19 C +ATOM 2711 O ALA A 343 -20.637 -0.323 -7.940 1.00 89.19 O +ATOM 2712 N GLU A 344 -19.223 -0.279 -6.190 1.00 72.75 N +ATOM 2713 CA GLU A 344 -19.643 -1.553 -5.579 1.00 72.75 C +ATOM 2714 C GLU A 344 -20.539 -1.375 -4.341 1.00 72.75 C +ATOM 2715 CB GLU A 344 -18.394 -2.350 -5.172 1.00 72.75 C +ATOM 2716 O GLU A 344 -20.982 -2.366 -3.758 1.00 72.75 O +ATOM 2717 CG GLU A 344 -17.454 -2.705 -6.331 1.00 72.75 C +ATOM 2718 CD GLU A 344 -18.124 -3.610 -7.375 1.00 72.75 C +ATOM 2719 OE1 GLU A 344 -17.918 -3.320 -8.579 1.00 72.75 O +ATOM 2720 OE2 GLU A 344 -18.780 -4.593 -6.984 1.00 72.75 O +ATOM 2721 N CYS A 345 -20.762 -0.134 -3.891 1.00 77.69 N +ATOM 2722 CA CYS A 345 -21.385 0.171 -2.598 1.00 77.69 C +ATOM 2723 C CYS A 345 -22.812 0.717 -2.711 1.00 77.69 C +ATOM 2724 CB CYS A 345 -20.455 1.081 -1.786 1.00 77.69 C +ATOM 2725 O CYS A 345 -23.129 1.417 -3.697 1.00 77.69 O +ATOM 2726 SG CYS A 345 -18.951 0.150 -1.371 1.00 77.69 S +ATOM 2727 OXT CYS A 345 -23.580 0.410 -1.770 1.00 77.69 O +TER 2728 CYS A 345 +ENDMDL +END \ No newline at end of file diff --git a/assets/test_structure/README.md b/assets/test_structure/README.md new file mode 100644 index 0000000..0452a06 --- /dev/null +++ b/assets/test_structure/README.md @@ -0,0 +1,14 @@ +# Test structure fixture + +`GID1A_AFDB.pdb` is the AlphaFold DB predicted model for GID1A (UniProt **Q9MAA7**), +downloaded from https://alphafold.ebi.ac.uk/entry/Q9MAA7. + +It is bundled only so the `test` profile can exercise the interactive variant effect +inspection tool end-to-end without structure prediction. + +**Licence:** AlphaFold DB structures are distributed under **CC-BY-4.0** +(© EMBL-EBI & DeepMind). If reused, attribute AlphaFold DB / UniProt Q9MAA7 accordingly. + +> TODO (release): move this fixture to the `deepmutscan` branch of `nf-core/test-datasets` +> (e.g. `deepmutscan/testdata/GID1A_AFDB.pdb`) with the same attribution, then reference it in +> `conf/test.config` via `params.pipelines_testdata_base_path` and remove this committed copy. diff --git a/assets/variant_effect_inspection_tool/3Dmol-min.js b/assets/variant_effect_inspection_tool/3Dmol-min.js new file mode 100644 index 0000000..c63bd14 --- /dev/null +++ b/assets/variant_effect_inspection_tool/3Dmol-min.js @@ -0,0 +1,2 @@ +/*! For license information please see 3Dmol-min.js.LICENSE.txt */ +!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports["3Dmol"]=e():t["3Dmol"]=e()}(this,(()=>(()=>{var __webpack_modules__={620:function(){"use strict";!function(t){if(t.TextEncoder&&t.TextDecoder)return!1;function e(t="utf-8"){if("utf-8"!==t)throw new RangeError(`Failed to construct 'TextEncoder': The encoding label provided ('${t}') is invalid.`)}function i(t="utf-8",e={fatal:!1}){if("utf-8"!==t)throw new RangeError(`Failed to construct 'TextDecoder': The encoding label provided ('${t}') is invalid.`);if(e.fatal)throw new Error("Failed to construct 'TextDecoder': the 'fatal' option is unsupported.")}Object.defineProperty(e.prototype,"encoding",{value:"utf-8"}),e.prototype.encode=function(t,e={stream:!1}){if(e.stream)throw new Error("Failed to encode: the 'stream' option is unsupported.");let i=0;const r=t.length;let s=0,n=Math.max(32,r+(r>>1)+7),a=new Uint8Array(n>>3<<3);for(;i=55296&&e<=56319){if(i=55296&&e<=56319)continue}if(s+4>a.length){n+=8,n*=1+i/t.length*2,n=n>>3<<3;const e=new Uint8Array(n);e.set(a),a=e}if(4294967168&e){if(4294965248&e)if(4294901760&e){if(4292870144&e)continue;a[s++]=e>>18&7|240,a[s++]=e>>12&63|128,a[s++]=e>>6&63|128}else a[s++]=e>>12&15|224,a[s++]=e>>6&63|128;else a[s++]=e>>6&31|192;a[s++]=63&e|128}else a[s++]=e}return a.slice(0,s)},Object.defineProperty(i.prototype,"encoding",{value:"utf-8"}),Object.defineProperty(i.prototype,"fatal",{value:!1}),Object.defineProperty(i.prototype,"ignoreBOM",{value:!1}),i.prototype.decode=function(t,e={stream:!1}){if(e.stream)throw new Error("Failed to decode: the 'stream' option is unsupported.");const i=new Uint8Array(t);let r=0;const s=i.length,n=[];for(;r65535&&(e-=65536,n.push(e>>>10&1023|55296),e=56320|1023&e),n.push(e)}}else n.push(t)}return String.fromCharCode.apply(null,n)},t.TextEncoder=e,t.TextDecoder=i}("undefined"!=typeof window?window:"undefined"!=typeof self?self:this)},546:(t,e,i)=>{"use strict";i.r(e),i.d(e,{CustomLinear:()=>CustomLinear,Gradient:()=>Gradient,GradientType:()=>GradientType,ROYGB:()=>ROYGB,RWB:()=>RWB,Sinebow:()=>Sinebow,builtinGradients:()=>a,getGradient:()=>n,normalizeValue:()=>s});var r=i(222);class GradientType{}function s(t,e,i){return e>=t?(ie&&(i=e),{lo:t,hi:e,val:i}):(i>t&&(i=t),i=2?(this.max=t[1],this.min=t[0]):t&&e&&!Array.isArray(t)&&(this.min=t,this.max=e)}range(){return void 0!==this.min&&void 0!==this.max?[this.min,this.max]:null}valueToHex(t,e){var i,r;if(t=this.mult*t,e?(i=e[0],r=e[1]):(i=this.min,r=this.max),void 0===t)return 16777215;var n=s(i,r,t);i=n.lo;var a,o=((r=n.hi)+i)/2;return(t=n.val)<(o=e&&void 0!==e[2]?e[2]:void 0!==this.mid?this.mid:(i+r)/2)?16711680+256*(a=Math.floor(255*Math.sqrt((t-i)/(o-i))))+a:t>o?65536*(a=Math.floor(255*Math.sqrt(1-(t-o)/(r-o))))+256*a+255:16777215}}class ROYGB extends GradientType{constructor(t,e){super(),this.gradient="ROYGB",this.mult=1,this.min=t,this.max=e,void 0===e&&Array.isArray(t)&&t.length>=2?(this.max=t[1],this.min=t[0]):t&&e&&!Array.isArray(t)&&(this.min=t,this.max=e)}valueToHex(t,e){var i,r;if(t=this.mult*t,e?(i=e[0],r=e[1]):(i=this.min,r=this.max),void 0===t)return 16777215;var n=s(i,r,t),a=((i=n.lo)+(r=n.hi))/2,o=(i+a)/2,l=(a+r)/2;return(t=n.val)=2&&(this.max=t[1],this.min=t[0]),e=2?(this.max=t[1],this.min=t[0],s=e):(this.min=t,this.max=e,s=i),s)for(let t of s)this.colors.push(r.CC.color(t));else this.colors.push(r.CC.color(0))}range(){return void 0!==this.min&&void 0!==this.max?[this.min,this.max]:null}valueToHex(t,e){var i,n;if(e?(i=e[0],n=e[1]):(i=this.min,n=this.max),void 0===t)return 16777215;var a=s(i,n,t);i=a.lo,n=a.hi,t=a.val;let o=this.colors.length,l=(n-i)/o,h=Math.min(Math.floor((t-i)/l),o-1),c=Math.min(h+1,o-1),d=(t-i-h*l)/l,u=this.colors[h],f=this.colors[c];return new r.Color(u.r+d*(f.r-u.r),u.g+d*(f.g-u.g),u.b+d*(f.b-u.b)).getHex()}}const a={rwb:RWB,RWB,roygb:ROYGB,ROYGB,sinebow:Sinebow,linear:CustomLinear};class Gradient extends GradientType{valueToHex(t,e){return 0}range(){return null}}Gradient.RWB=RWB,Gradient.ROYGB=ROYGB,Gradient.Sinebow=Sinebow,Gradient.CustomLinear=CustomLinear,Gradient.builtinGradients=a,Gradient.normalizeValue=s,Gradient.getGradient=n},848:(t,e,i)=>{"use strict";i.r(e),i.d(e,{VolumeData:()=>VolumeData});var r=i(864),s=i(529),n=i(797),a=i(865),o=i(75);class VolumeData{constructor(t,e,i){if(this.unit={x:1,y:1,z:1},this.origin={x:0,y:0,z:0},this.size={x:0,y:0,z:0},this.data=new Float32Array([]),this.matrix=null,this.inversematrix=null,this.isbinary=new Set(["ccp4","CCP4"]),this.getCoordinates=function(t){var e=t/(this.size.y*this.size.z),i=t%(this.size.y*this.size.z),r=t%this.size.z;return e*=this.unit.x,i*=this.unit.y,r*=this.unit.z,{x:e+=this.origin.x,y:i+=this.origin.y,z:r+=this.origin.z}},this.vasp=function(t){var e=t.replace(/^\s+/,"").split(/[\n\r]/),i=(0,n.VASP)(t)[0].length;if(0==i)return console.warn("No good formating of CHG or CHGCAR file, not atomic information provided in the file."),void(this.data=[]);var r,a=1.889725992,o=parseFloat(e[1]);r=e[2].replace(/^\s+/,"").split(/\s+/);var l=new s.Vector3(parseFloat(r[0]),parseFloat(r[1]),parseFloat(r[2])).multiplyScalar(o*a);r=e[3].replace(/^\s+/,"").split(/\s+/);var h=new s.Vector3(parseFloat(r[0]),parseFloat(r[1]),parseFloat(r[2])).multiplyScalar(o*a);r=e[4].replace(/^\s+/,"").split(/\s+/);var c=new s.Vector3(parseFloat(r[0]),parseFloat(r[1]),parseFloat(r[2])).multiplyScalar(o*a),d=l.x*(h.y*c.z-c.y*h.z)-h.x*(l.y*c.z-c.y*l.z)+c.x*(l.y*h.z-h.y*l.z),u=1/(d=Math.abs(d)/Math.pow(a,3));e.splice(0,8+i+1);var f=e[0].replace(/^\s+/,"").replace(/\s+/g," ").split(" "),p=Math.abs(parseFloat(f[0])),g=Math.abs(parseFloat(f[1])),m=Math.abs(parseFloat(f[2])),v=this.origin=new s.Vector3(0,0,0);this.size={x:p,y:g,z:m},this.unit=new s.Vector3(l.x,h.y,c.z),l=l.multiplyScalar(1/(a*p)),h=h.multiplyScalar(1/(a*g)),c=c.multiplyScalar(1/(a*m)),0==l.y&&0==l.z&&0==h.x&&0==h.z&&0==c.x&&0==c.y||(this.matrix=new s.Matrix4(l.x,h.x,c.x,0,l.y,h.y,c.y,0,l.z,h.z,c.z,0,0,0,0,1),this.matrix=this.matrix.multiplyMatrices(this.matrix,(new s.Matrix4).makeTranslation(v.x,v.y,v.z)),this.origin=new s.Vector3(0,0,0),this.unit=new s.Vector3(1,1,1)),e.splice(0,1);var _=e.join(" "),y=(_=_.replace(/^\s+/,"")).split(/[\s\r]+/);y.splice(p*g*m+1);for(var b=Float32Array.from(y,parseFloat),x=0;x=this.size.x||e<0||e>=this.size.y||i<0||i>=this.size.z?-1:t*this.size.y*this.size.z+e*this.size.z+i}getVal(t,e,i){let r=this.getIndex(t,e,i);return r<0?0:this.data[r]}cube(t){var e=t.split(/\r?\n/);if(!(e.length<6)){var i=(0,a.CUBE)(t,{}).modelData[0].cryst,r=e[2].replace(/^\s+/,"").replace(/\s+/g," ").split(" "),s=parseFloat(r[0]),n=Math.abs(s);this.origin=i.origin,this.size=i.size,this.unit=i.unit,this.matrix=i.matrix4;var o=6;s<0&&o++;var l=e.splice(n+o).join(" "),h=(l=l.replace(/^\s+/,"")).split(/[\s\r]+/);this.data=Float32Array.from(h,parseFloat)}}ccp4(t){var e={};t=new Int8Array(t);var i=new Int32Array(t.buffer,0,56),r=new Float32Array(t.buffer,0,56),n=new DataView(t.buffer);if(e.MAP=String.fromCharCode(n.getUint8(208),n.getUint8(209),n.getUint8(210),n.getUint8(211)),e.MACHST=[n.getUint8(212),n.getUint8(213)],17===e.MACHST[0]&&17===e.MACHST[1])for(var a=t.byteLength,o=0;o{"use strict";var r;i.r(e),i.d(e,{BackSide:()=>n,Camera:()=>Camera,ClampToEdgeWrapping:()=>c,Coloring:()=>r,Cylinder:()=>M.Cylinder,DoubleSide:()=>a,EventDispatcher:()=>EventDispatcher,FloatType:()=>g,Fog:()=>Fog,FrontSide:()=>s,Geometry:()=>Geometry,GeometryGroup:()=>GeometryGroup,GeometryIDCount:()=>A,ImposterMaterial:()=>ImposterMaterial,InstancedMaterial:()=>InstancedMaterial,Light:()=>Light,Line:()=>Line,LineBasicMaterial:()=>LineBasicMaterial,LineStyle:()=>N,LinearFilter:()=>d,LinearMipMapLinearFilter:()=>f,Material:()=>Material,MaterialIdCount:()=>x,Matrix3:()=>l.Matrix3,Matrix4:()=>l.Matrix4,Mesh:()=>Mesh,MeshDoubleLambertMaterial:()=>MeshDoubleLambertMaterial,MeshLambertMaterial:()=>MeshLambertMaterial,MeshOutlineMaterial:()=>MeshOutlineMaterial,NearestFilter:()=>u,Object3D:()=>Object3D,Object3DIDCount:()=>C,Projector:()=>Projector,Quaternion:()=>l.Quaternion,R32Format:()=>_,RFormat:()=>v,RGBAFormat:()=>m,Ray:()=>l.Ray,Raycaster:()=>Raycaster,Renderer:()=>Renderer,Scene:()=>Scene,ShaderLib:()=>ct,ShaderUtils:()=>ut,Shading:()=>o,Sphere:()=>M.Sphere,SphereImposterMaterial:()=>SphereImposterMaterial,SphereImposterOutlineMaterial:()=>SphereImposterOutlineMaterial,Sprite:()=>Sprite,SpriteAlignment:()=>h,SpriteMaterial:()=>SpriteMaterial,SpritePlugin:()=>SpritePlugin,StickImposterMaterial:()=>StickImposterMaterial,StickImposterOutlineMaterial:()=>StickImposterOutlineMaterial,Texture:()=>Texture,TextureIdCount:()=>B,TextureOperations:()=>y,Triangle:()=>M.Triangle,UVMapping:()=>UVMapping,UnsignedByteType:()=>p,Vector2:()=>l.Vector2,Vector3:()=>l.Vector3,VolumetricMaterial:()=>VolumetricMaterial,basic:()=>V,clamp:()=>l.clamp,clone:()=>dt,conversionMatrix3:()=>l.conversionMatrix3,degToRad:()=>l.degToRad,instanced:()=>H,intersectObject:()=>U,lambert:()=>q,lambertdouble:()=>Z,outline:()=>K,screen:()=>Q,screenaa:()=>$,sphereimposter:()=>tt,sphereimposteroutline:()=>it,sprite:()=>rt,stickimposter:()=>at,stickimposteroutline:()=>ot,volumetric:()=>ht}),function(t){t[t.NoColors=0]="NoColors",t[t.FaceColors=1]="FaceColors",t[t.VertexColors=2]="VertexColors"}(r||(r={}));const s=0,n=1,a=2;var o;!function(t){t[t.NoShading=0]="NoShading",t[t.FlatShading=1]="FlatShading",t[t.SmoothShading=2]="SmoothShading"}(o||(o={}));var l=i(529);const h={topLeft:new l.Vector2(1,-1),topCenter:new l.Vector2(0,-1),topRight:new l.Vector2(-1,-1),centerLeft:new l.Vector2(1,0),center:new l.Vector2(0,0),centerRight:new l.Vector2(-1,0),bottomLeft:new l.Vector2(1,1),bottomCenter:new l.Vector2(0,1),bottomRight:new l.Vector2(-1,1)},c=1001,d=1006,u=1007,f=1008,p=1009,g=1010,m=1021,v=1022,_=1023;var y;!function(t){t[t.MultiplyOperation=0]="MultiplyOperation",t[t.MixOperation=1]="MixOperation",t[t.AddOperation=2]="AddOperation"}(y||(y={}));class EventDispatcher{constructor(){this.listeners={}}dispatchEvent(t){var e=this.listeners[t.type];if(void 0!==e){t.target=this;for(var i=0,r=e.length;i0?this.lineArray=(null==a?void 0:a.subarray(0,this.lineidx))||null:this.lineArray=new Uint16Array(0)):(this.normalArray=new Float32Array(0),this.faceArray=new Uint16Array(0),this.lineArray=new Uint16Array(0)),o&&(this.radiusArray=o.subarray(0,this.vertices)),e&&(this.normalArray&&(this.normalArray=new Float32Array(this.normalArray)),this.faceArray&&(this.faceArray=new Uint16Array(this.faceArray)),this.lineArray&&(this.lineArray=new Uint16Array(this.lineArray)),this.vertexArray&&(this.vertexArray=new Float32Array(this.vertexArray)),this.colorArray&&(this.colorArray=new Float32Array(this.colorArray)),this.radiusArray&&(this.radiusArray=new Float32Array(this.radiusArray))),this.__inittedArrays=!0}}class Geometry extends EventDispatcher{constructor(t=!1,e=!1,i=!1){super(),this.name="",this.hasTangents=!1,this.dynamic=!0,this.verticesNeedUpdate=!1,this.elementsNeedUpdate=!1,this.normalsNeedUpdate=!1,this.colorsNeedUpdate=!1,this.buffersNeedUpdate=!1,this.imposter=!1,this.instanced=!1,this.geometryGroups=[],this.groups=0,this.id=A++,this.mesh=t,this.radii=e,this.offset=i}updateGeoGroup(t=0){var e,i=this.groups>0?this.geometryGroups[this.groups-1]:null;return(!i||i.vertices+t>((null===(e=null==i?void 0:i.vertexArray)||void 0===e?void 0:e.length)||0)/3)&&(i=this.addGeoGroup()),i}vrml(t,e){for(var i="",r=this.geometryGroups.length,s=0;st.distance-e.distance,T=new l.Matrix4;class Raycaster{constructor(t,e,i,r){this.precision=1e-4,this.linePrecision=.2,this.ray=new l.Ray(t,e),this.ray.direction.lengthSq()>0&&this.ray.direction.normalize(),this.near=r||0,this.far=i||1/0}set(t,e){this.ray.set(t,e)}setFromCamera(t,e){e.ortho?(this.ray.origin.set(t.x,t.y,(e.near+e.far)/(e.near-e.far)).unproject(e),this.ray.direction.set(0,0,-1).transformDirection(e.matrixWorld)):(this.ray.origin.setFromMatrixPosition(e.matrixWorld),this.ray.direction.set(t.x,t.y,t.z),e.projectionMatrixInverse.getInverse(e.projectionMatrix),T.multiplyMatrices(e.matrixWorld,e.projectionMatrixInverse),this.ray.direction.applyProjection(T),this.ray.direction.sub(this.ray.origin).normalize())}intersectObjects(t,e){for(var i=[],r=0,s=e.length;rMath.min(Math.max(t,-1),1);var L=new M.Sphere,F=new M.Cylinder,I=new M.Triangle,O=new l.Vector3,D=new l.Vector3,k=new l.Vector3,R=new l.Vector3,P=new l.Vector3;function U(t,e,i,r){if(P.getPositionFromMatrix(t.matrixWorld),void 0===e.intersectionShape)return r;var s,n,a,o,l,h,c,d,u,f,p,g,m,v,_=e.intersectionShape,y=i.linePrecision,b=(y*=t.matrixWorld.getMaxScaleOnAxis())*y;if(void 0!==e.boundingSphere&&e.boundingSphere instanceof M.Sphere&&(L.copy(e.boundingSphere),L.applyMatrix4(t.matrixWorld),!i.ray.isIntersectionSphere(L)))return r;for(s=0,n=_.triangle.length;s=0)continue;if(O.subVectors(I.a,i.ray.origin),(c=a.dot(O)/o)<0)continue;D.copy(i.ray.direction).multiplyScalar(c).add(i.ray.origin),D.sub(I.a),k.copy(I.b).sub(I.a),R.copy(I.c).sub(I.a);var x=k.dot(R),w=k.lengthSq(),A=R.lengthSq();if((g=(w*D.dot(R)-x*D.dot(k))/(w*A-x*x))<0||g>1)continue;if((p=(D.dot(k)-g*x)/w)<0||p>1||p+g>1)continue;r.push({clickable:e,distance:c})}for(s=0,n=_.cylinder.length;sF.lengthSq()||g<0)continue;r.push({clickable:e,distance:c})}}for(s=0,n=_.line.length;s 0.0) {\n vec4 unadjusted = projectionMatrix*modelViewMatrix * vec4( position, 1.0 );\n float w = outpos.w;\n //normalize homogeneous coords\n unadjusted /= unadjusted.w;\n outpos /= outpos.w;\n vec2 diff = outpos.xy-unadjusted.xy;\n //put into pixels\n diff.x *= vWidth;\n diff.y *= vHeight;\n if ( length(diff) > outlineMaxPixels) {\n vec2 ndiff = normalize(diff)*outlineMaxPixels;\n ndiff.x /= vWidth;\n ndiff.y /= vHeight;\n outpos.xy = unadjusted.xy;\n outpos.xy += ndiff;\n }\n outpos *= w; //if I don't do this things blow up\n }\n gl_Position = outpos;\n mvPosition.z -= outlinePushback; //go backwards in model space\n vec4 pushpos = projectionMatrix*mvPosition; //project to get z in projection space, I'm probably missing some simple math to do the same thing..\n gl_Position.z = gl_Position.w*pushpos.z/pushpos.w;\n}\n\n".replace("#define GLSLIFY 1",""),uniforms:X},Q={fragmentShader:"uniform sampler2D colormap;\nvarying highp vec2 vTexCoords;\nuniform vec2 dimensions;\n//DEFINEFRAGCOLOR\nvoid main (void) {\n gl_FragColor = texture2D(colormap, vTexCoords);\n\n //gl_FragColor.g = gl_FragColor.b = gl_FragColor.r; //debug shading \n}\n ".replace("#define GLSLIFY 1",""),vertexShader:"attribute vec2 vertexPosition;\nvarying highp vec2 vTexCoords;\nconst vec2 scale = vec2(0.5, 0.5);\n\nvoid main() {\n vTexCoords = vertexPosition * scale + scale; // scale vertex attribute to [0,1] range\n gl_Position = vec4(vertexPosition, 0.0, 1.0);\n}\n ".replace("#define GLSLIFY 1",""),uniforms:{}},$={fragmentShader:"uniform highp sampler2D colormap;\nvarying highp vec2 vTexCoords;\n\n\n// Basic FXAA implementation based on the code on geeks3d.com \n#define FXAA_REDUCE_MIN (1.0/ 128.0)\n#define FXAA_REDUCE_MUL (1.0 / 8.0)\n#define FXAA_SPAN_MAX 8.0\n\n\nvec4 applyFXAA(vec2 fragCoord, highp sampler2D tex)\n{\n vec4 color;\n ivec2 dim = textureSize(tex,0);\n vec2 dimensions = vec2(float(dim.x),float(dim.y));\n vec2 inverseVP = vec2(1.0 / dimensions.x, 1.0 / dimensions.y);\n vec4 rgbNW = texture2D(tex, fragCoord + vec2(-1.0, -1.0) * inverseVP);\n vec4 rgbNE = texture2D(tex, fragCoord + vec2(1.0, -1.0) * inverseVP);\n vec4 rgbSW = texture2D(tex, fragCoord + vec2(-1.0, 1.0) * inverseVP);\n vec4 rgbSE = texture2D(tex, fragCoord + vec2(1.0, 1.0) * inverseVP);\n vec4 rgbM = texture2D(tex, fragCoord );\n vec3 luma = vec3(0.299, 0.587, 0.114);\n float lumaNW = dot(rgbNW.xyz, luma);\n float lumaNE = dot(rgbNE.xyz, luma);\n float lumaSW = dot(rgbSW.xyz, luma);\n float lumaSE = dot(rgbSE.xyz, luma);\n float lumaM = dot(rgbM.xyz, luma);\n float lumaMin = min(lumaM, min(min(lumaNW, lumaNE), min(lumaSW, lumaSE)));\n float lumaMax = max(lumaM, max(max(lumaNW, lumaNE), max(lumaSW, lumaSE)));\n\n vec2 dir;\n dir.x = -((lumaNW + lumaNE) - (lumaSW + lumaSE));\n dir.y = ((lumaNW + lumaSW) - (lumaNE + lumaSE));\n\n float dirReduce = max((lumaNW + lumaNE + lumaSW + lumaSE) *\n (0.25 * FXAA_REDUCE_MUL), FXAA_REDUCE_MIN);\n\n float rcpDirMin = 1.0 / (min(abs(dir.x), abs(dir.y)) + dirReduce);\n dir = min(vec2(FXAA_SPAN_MAX, FXAA_SPAN_MAX),\n max(vec2(-FXAA_SPAN_MAX, -FXAA_SPAN_MAX),\n dir * rcpDirMin)) * inverseVP;\n\n vec4 rgbA = 0.5 * (\n texture2D(tex, fragCoord + dir * (1.0 / 3.0 - 0.5)) +\n texture2D(tex, fragCoord + dir * (2.0 / 3.0 - 0.5)));\n vec4 rgbB = rgbA * 0.5 + 0.25 * (\n texture2D(tex, fragCoord + dir * -0.5) +\n texture2D(tex, fragCoord + dir * 0.5));\n\n float lumaB = dot(rgbB.xyz, luma);\n if ((lumaB < lumaMin) || (lumaB > lumaMax))\n color = rgbA;\n else\n color = rgbB;\n\n return color;\n}\n\n\n//DEFINEFRAGCOLOR\nvoid main (void) {\n ivec2 dim = textureSize(colormap,0);\n\n gl_FragColor = applyFXAA(vTexCoords, colormap);\n}\n ".replace("#define GLSLIFY 1",""),vertexShader:"attribute vec2 vertexPosition;\nvarying highp vec2 vTexCoords;\nconst vec2 scale = vec2(0.5, 0.5);\n\nvoid main() {\n vTexCoords = vertexPosition * scale + scale; // scale vertex attribute to [0,1] range\n gl_Position = vec4(vertexPosition, 0.0, 1.0);\n}\n ".replace("#define GLSLIFY 1",""),uniforms:{}},J={opacity:{type:"f",value:1},fogColor:{type:"c",value:new b.Color(1,1,1)},fogNear:{type:"f",value:1},fogFar:{type:"f",value:2e3},directionalLightColor:{type:"fv",value:[]},directionalLightDirection:{type:"fv",value:[]}},tt={vertexShader:"uniform mat4 modelViewMatrix;\nuniform mat4 projectionMatrix;\nuniform mat4 viewMatrix;\nuniform mat3 normalMatrix;\nuniform vec3 directionalLightColor[ 1 ];\nuniform vec3 directionalLightDirection[ 1 ];\n\nattribute vec3 position;\nattribute vec3 normal;\nattribute vec3 color;\n\nvarying vec2 mapping;\nvarying vec3 vColor;\nvarying float rval;\nvarying vec3 vLight;\nvarying vec3 center;\n\nvoid main() {\n\n vColor = color;\n vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );\n center = mvPosition.xyz;\n vec4 projPosition = projectionMatrix * mvPosition;\n vec4 adjust = projectionMatrix* vec4(normal,0.0); adjust.z = 0.0; adjust.w = 0.0;\n vec4 lDirection = viewMatrix * vec4( directionalLightDirection[ 0 ], 0.0 );\n vLight = normalize( lDirection.xyz );\n mapping = normal.xy;\n rval = abs(normal.x);\n gl_Position = projPosition+adjust;\n\n}\n".replace("#define GLSLIFY 1",""),fragmentShader:"\nuniform mat4 viewMatrix;\nuniform float opacity;\nuniform mat4 projectionMatrix;\n\nuniform vec3 fogColor;\nuniform float fogNear;\nuniform float fogFar;\nuniform float uDepth;\nuniform vec3 directionalLightColor[ 1 ];\n\nvarying vec3 vColor;\nvarying vec2 mapping;\nvarying float rval;\nvarying vec3 vLight;\nvarying vec3 center;\n\n#ifdef SHADED\nuniform highp sampler2D shading;\n#endif\n\n//DEFINEFRAGCOLOR\n\nvoid main() {\n float lensqr = dot(mapping,mapping);\n float rsqr = rval*rval;\n if(lensqr > rsqr)\n discard;\n float z = sqrt(rsqr-lensqr);\n vec3 cameraPos = center+ vec3(mapping.x,mapping.y,z);\n vec4 clipPos = projectionMatrix * vec4(cameraPos, 1.0);\n float ndcDepth = clipPos.z / clipPos.w;\n gl_FragDepthEXT = ((gl_DepthRange.diff * ndcDepth) + gl_DepthRange.near + gl_DepthRange.far) / 2.0;\n vec3 norm = normalize(vec3(mapping.x,mapping.y,z));\n float dotProduct = dot( norm, vLight );\n vec3 directionalLightWeighting = vec3( max( dotProduct, 0.0 ) );\n vec3 vLight = directionalLightColor[ 0 ] * directionalLightWeighting;\n vec3 color = vLight*vColor;\n#ifdef SHADED\n ivec2 dim = textureSize(shading,0);\n float shadowFactor = texture2D(shading,vec2(gl_FragCoord.x/float(dim.x),gl_FragCoord.y/float(dim.y))).r;\n color *= shadowFactor;\n#endif \n gl_FragColor = vec4(color, opacity*opacity );\n float fogFactor = smoothstep( fogNear, fogFar, gl_FragDepthEXT/gl_FragCoord.w );\n gl_FragColor = mix( gl_FragColor, vec4( fogColor, gl_FragColor.w ), fogFactor );\n}\n\n".replace("#define GLSLIFY 1",""),uniforms:J},et={opacity:{type:"f",value:1},outlineColor:{type:"c",value:new b.Color(0,0,0)},fogColor:{type:"c",value:new b.Color(1,1,1)},fogNear:{type:"f",value:1},fogFar:{type:"f",value:2e3},outlineWidth:{type:"f",value:.1},outlinePushback:{type:"f",value:1},outlineMaxPixels:{type:"f",value:0}},it={fragmentShader:"\n\nuniform float opacity;\nuniform vec3 outlineColor;\nuniform vec3 fogColor;\nuniform float fogNear;\nuniform float fogFar;\nuniform mat4 projectionMatrix;\nvarying vec2 mapping;\nvarying float rval;\nvarying vec3 center;\n\nuniform float outlinePushback;\n\n//DEFINEFRAGCOLOR\n\nvoid main() {\n float lensqr = dot(mapping,mapping);\n float rsqr = rval*rval;\n if(lensqr > rsqr)\n discard;\n float z = sqrt(rsqr-lensqr);\n vec3 cameraPos = center+ vec3(mapping.x,mapping.y,z-outlinePushback);\n vec4 clipPos = projectionMatrix * vec4(cameraPos, 1.0);\n float ndcDepth = clipPos.z / clipPos.w;\n gl_FragDepthEXT = ((gl_DepthRange.diff * ndcDepth) + gl_DepthRange.near + gl_DepthRange.far) / 2.0;\n gl_FragColor = vec4(outlineColor, 1 );\n}\n\n\n".replace("#define GLSLIFY 1",""),vertexShader:"\n\nuniform mat4 modelViewMatrix;\nuniform mat4 projectionMatrix;\nuniform float outlineWidth;\nuniform float outlinePushback;\nuniform float outlineMaxPixels;\nuniform float vWidth;\nuniform float vHeight;\n\nattribute vec3 position;\nattribute vec3 normal;\nattribute vec3 color;\n\nvarying vec2 mapping;\nvarying float rval;\nvarying vec3 center;\n\nvoid main() {\n\n vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );\n center = mvPosition.xyz;\n vec4 projPosition = projectionMatrix * mvPosition;\n vec2 norm = normal.xy + vec2(sign(normal.x)*outlineWidth,sign(normal.y)*outlineWidth);\n\n vec4 adjust = projectionMatrix* vec4(norm,normal.z,1.0); \n mapping = norm.xy;\n rval = abs(norm.x);\n gl_Position = projPosition+vec4(adjust.xy,0.0,0.0);\n\n if(outlineMaxPixels > 0.0) {\n vec4 unadjusted = projectionMatrix*vec4(center.x+normal.x, center.y,center.z,1.0); \n vec4 ccoord = projectionMatrix*vec4(center.xyz,1.0);\n adjust = projectionMatrix* vec4(center.x+norm.x,center.y,center.z,1.0); \n //subtract center \n unadjusted.xyz -= ccoord.xyz;\n adjust.xyz -= ccoord.xyz;\n unadjusted /= unadjusted.w;\n adjust /= adjust.w;\n float diff = abs(adjust.x-unadjusted.x);\n diff *= vWidth;\n if(diff > outlineMaxPixels) {\n \n float fixlen = abs(unadjusted.x) + outlineMaxPixels/vWidth;\n //adjsut reval by ratio of lengths\n rval *= fixlen/abs(adjust.x);\n }\n\n }\n}\n\n".replace("#define GLSLIFY 1",""),uniforms:et},rt={fragmentShader:"\n\nuniform vec3 color;\nuniform sampler2D map;\nuniform float opacity;\n\nuniform int fogType;\nuniform vec3 fogColor;\nuniform float fogDensity;\nuniform float fogNear;\nuniform float fogFar;\nuniform float alphaTest;\n\nvarying vec2 vUV;\n//DEFINEFRAGCOLOR\n\nvoid main() {\n\n vec4 texture = texture2D(map, vUV);\n\n if (texture.a <= alphaTest) discard;\n\n gl_FragColor = vec4(color * texture.xyz, texture.a * opacity);\n\n if (fogType > 0) {\n\n float depth = gl_FragCoord.z / gl_FragCoord.w;\n float fogFactor = 0.0;\n\n if (fogType == 1) {\n fogFactor = smoothstep(fogNear, fogFar, depth);\n }\n\n else {\n const float LOG2 = 1.442695;\n float fogFactor = exp2(- fogDensity * fogDensity * depth * depth * LOG2);\n fogFactor = 1.0 - clamp(fogFactor, 0.0, 1.0);\n }\n\n gl_FragColor = mix(gl_FragColor, vec4(fogColor, gl_FragColor.w), fogFactor);\n\n }\n}\n\n".replace("#define GLSLIFY 1",""),vertexShader:"\n\nuniform int useScreenCoordinates;\nuniform vec3 screenPosition;\nuniform mat4 modelViewMatrix;\nuniform mat4 projectionMatrix;\nuniform float rotation;\nuniform vec2 scale;\nuniform vec2 alignment;\nuniform vec2 uvOffset;\nuniform vec2 uvScale;\n\nattribute vec2 position;\nattribute vec2 uv;\n\nvarying vec2 vUV;\n\nvoid main() {\n\n vUV = uvOffset + uv * uvScale;\n\n vec2 alignedPosition = position + alignment;\n\n vec2 rotatedPosition;\n rotatedPosition.x = ( cos(rotation) * alignedPosition.x - sin(rotation) * alignedPosition.y ) * scale.x;\n rotatedPosition.y = ( sin(rotation) * alignedPosition.x + cos(rotation) * alignedPosition.y ) * scale.y;\n\n vec4 finalPosition;\n\n if(useScreenCoordinates != 0) {\n finalPosition = vec4(screenPosition.xy + rotatedPosition, screenPosition.z, 1.0);\n }\n\n else {\n finalPosition = projectionMatrix * modelViewMatrix * vec4(0.0, 0.0, 0.0, 1.0); finalPosition /= finalPosition.w;\n finalPosition.xy += rotatedPosition; \n }\n\n gl_Position = finalPosition;\n\n}\n\n".replace("#define GLSLIFY 1",""),uniforms:{}},st={opacity:{type:"f",value:1},fogColor:{type:"c",value:new b.Color(1,1,1)},fogNear:{type:"f",value:1},fogFar:{type:"f",value:2e3},directionalLightColor:{type:"fv",value:[]},directionalLightDirection:{type:"fv",value:[]}},nt="uniform float opacity;\nuniform mat4 projectionMatrix;\n\nuniform vec3 fogColor;\nuniform float fogNear;\nuniform float fogFar;\n\nvarying vec3 vLight;\nvarying vec3 vColor;\nvarying vec3 cposition;\nvarying vec3 p1;\nvarying vec3 p2;\nvarying float r;\n\n#ifdef SHADED\nuniform highp sampler2D shading;\n#endif\n\n//DEFINEFRAGCOLOR\n\n//cylinder-ray intersection testing taken from http://mrl.nyu.edu/~dzorin/cg05/lecture12.pdf\n//also useful: http://stackoverflow.com/questions/9595300/cylinder-impostor-in-glsl\n//with a bit more care (caps) this could be a general cylinder imposter (see also outline)\nvoid main() {\n vec3 color = abs(vColor);\n vec3 pos = cposition;\n vec3 p = pos; //ray point\n vec3 v = vec3(0.0,0.0,-1.0); //ray normal - orthographic\n if(projectionMatrix[3][3] == 0.0) v = normalize(pos); //ray normal - perspective\n vec3 pa = p1; //cyl start\n vec3 va = normalize(p2-p1); //cyl norm\n vec3 tmp1 = v-(dot(v,va)*va);\n vec3 deltap = p-pa;\n float A = dot(tmp1,tmp1);\n if(A == 0.0) discard;\n vec3 tmp2 = deltap-(dot(deltap,va)*va);\n float B = 2.0*dot(tmp1, tmp2);\n float C = dot(tmp2,tmp2)-r*r;\n//quadratic equation!\n float det = (B*B) - (4.0*A*C);\n if(det < 0.0) discard;\n float sqrtDet = sqrt(det);\n float posT = (-B+sqrtDet)/(2.0*A);\n float negT = (-B-sqrtDet)/(2.0*A);\n float intersectionT = min(posT,negT);\n vec3 qi = p+v*intersectionT;\n float dotp1 = dot(va,qi-p1);\n float dotp2 = dot(va,qi-p2);\n vec3 norm;\n if( dotp1 < 0.0 || dotp2 > 0.0) { //(p-c)^2 + 2(p-c)vt +v^2+t^2 - r^2 = 0\n vec3 cp;\n if( dotp1 < 0.0) { \n// if(vColor.x < 0.0 ) discard; //color sign bit indicates if we should cap or not\n cp = p1;\n } else {\n// if(vColor.y < 0.0 ) discard;\n cp = p2;\n }\n vec3 diff = p-cp;\n A = dot(v,v);\n B = dot(diff,v)*2.0;\n C = dot(diff,diff)-r*r;\n det = (B*B) - (4.0*C);\n if(det < 0.0) discard;\n sqrtDet = sqrt(det);\n posT = (-B+sqrtDet)/(2.0);\n negT = (-B-sqrtDet)/(2.0);\n float t = min(posT,negT);\n qi = p+v*t; \n norm = normalize(qi-cp); \n } else {\n norm = normalize(qi-(dotp1*va + p1));\n }\n vec4 clipPos = projectionMatrix * vec4(qi, 1.0);\n float ndcDepth = clipPos.z / clipPos.w;\n float depth = ((gl_DepthRange.diff * ndcDepth) + gl_DepthRange.near + gl_DepthRange.far) / 2.0;\n gl_FragDepthEXT = depth;",at={fragmentShader:[nt," float dotProduct = dot( norm, vLight );\n vec3 light = vec3( max( dotProduct, 0.0 ) );\n color *= light;\n#ifdef SHADED\n ivec2 dim = textureSize(shading,0);\n float shadowFactor = texture2D(shading,vec2(gl_FragCoord.x/float(dim.x),gl_FragCoord.y/float(dim.y))).r;\n color *= shadowFactor;\n#endif \n gl_FragColor = vec4(color, opacity*opacity );\n float fogFactor = smoothstep( fogNear, fogFar, depth );\n gl_FragColor = mix( gl_FragColor, vec4( fogColor, gl_FragColor.w ), fogFactor );\n}"].join("\n"),vertexShader:"\n\nuniform mat4 modelViewMatrix;\nuniform mat4 projectionMatrix;\nuniform mat4 viewMatrix;\nuniform mat3 normalMatrix;\nuniform vec3 directionalLightColor[ 1 ];\nuniform vec3 directionalLightDirection[ 1 ];\n\nattribute vec3 position;\nattribute vec3 normal;\nattribute vec3 color;\nattribute float radius;\n\nvarying vec3 vColor;\nvarying vec3 vLight;\nvarying vec3 cposition;\nvarying vec3 p1;\nvarying vec3 p2;\nvarying float r;\n\nvoid main() {\n\n vColor = color; vColor.z = abs(vColor.z); //z indicates which vertex and so would vary\n r = abs(radius);\n vec4 to = modelViewMatrix*vec4(normal, 1.0); //normal is other point of cylinder\n vec4 pt = modelViewMatrix*vec4(position, 1.0);\n vec4 mvPosition = pt;\n p1 = pt.xyz; p2 = to.xyz;\n vec3 norm = to.xyz-pt.xyz;\n float mult = 1.1; //slop to account for perspective of sphere\n if(length(p1) > length(p2)) { //billboard at level of closest point\n mvPosition = to;\n }\n vec3 n = normalize(mvPosition.xyz);\n bool isperspective = (projectionMatrix[3][3] == 0.0);\n//intersect with the plane defined by the camera looking at the billboard point\n if(color.z >= 0.0) { //p1\n if(isperspective) { //perspective\n vec3 pnorm = normalize(p1);\n float t = dot(mvPosition.xyz-p1,n)/dot(pnorm,n);\n mvPosition.xyz = p1+t*pnorm; \n } else { //orthographic\n mvPosition.xyz = p1;\n }\n } else {\n if(isperspective) { //perspective\n vec3 pnorm = normalize(p2);\n float t = dot(mvPosition.xyz-p2,n)/dot(pnorm,n);\n mvPosition.xyz = p2+t*pnorm;\n } else { //orthographic\n mvPosition.xyz = p2;\n } \n mult *= -1.0;\n }\n\n if(isperspective) { //perspective\n vec3 cr = normalize(cross(mvPosition.xyz,norm))*radius;\n vec3 doublecr = normalize(cross(mvPosition.xyz,cr))*radius;\n mvPosition.xyz += mult*(cr + doublecr).xyz;\n } else {\n vec3 cr = normalize(cross(vec3(0.0,0.0,-1.0),norm))*radius;\n vec3 doublecr = normalize(cross(vec3(0.0,0.0,-1.0),cr))*radius;\n mvPosition.xyz += mult*(cr + doublecr).xyz; \n }\n\n cposition = mvPosition.xyz;\n gl_Position = projectionMatrix * mvPosition;\n vec4 lDirection = viewMatrix * vec4( directionalLightDirection[ 0 ], 0.0 );\n vLight = normalize( lDirection.xyz )*directionalLightColor[0]; //not really sure this is right, but color is always white so..\n}\n\n",uniforms:st},ot={fragmentShader:nt+"gl_FragColor = vec4(color,1.0);}",vertexShader:"\n\nuniform mat4 modelViewMatrix;\nuniform mat4 projectionMatrix;\nuniform mat4 viewMatrix;\nuniform mat3 normalMatrix;\nuniform vec3 directionalLightColor[ 1 ];\nuniform vec3 directionalLightDirection[ 1 ];\nuniform vec3 outlineColor;\nuniform float outlineWidth;\nuniform float outlinePushback;\nuniform float outlineMaxPixels;\nuniform float vWidth;\nuniform mat4 projinv;\n\n\nattribute vec3 position;\nattribute vec3 normal;\nattribute vec3 color;\nattribute float radius;\n\nvarying vec3 vColor;\nvarying vec3 vLight;\nvarying vec3 cposition;\nvarying vec3 p1;\nvarying vec3 p2;\nvarying float r;\n\nvoid main() {\n\n vColor = outlineColor;\n float rad = radius+sign(radius)*outlineWidth;\n r = abs(rad);\n\n vec4 to = modelViewMatrix*vec4(normal, 1.0); //normal is other point of cylinder\n vec4 pt = modelViewMatrix*vec4(position, 1.0);\n//pushback\n float scale = 1.0;\n if(projectionMatrix[3][3] != 0.0) { //orthographic\n to.z -= outlinePushback;\n pt.z -= outlinePushback;\n } else { //perspective\n vec4 midbefore = pt;\n if(length(to.xyz) < length(pt)) {\n midbefore = to;\n }\n vec4 midafter = midbefore;\n midafter.xyz += normalize(midbefore.xyz)*outlinePushback;\n\n to.xyz += normalize(to.xyz)*outlinePushback;\n pt.xyz += normalize(pt.xyz)*outlinePushback;\n\n //figure out a scaling factor for radius to account for perspective setback\n vec4 midbeforer = vec4(midbefore.x+rad,midbefore.y, midbefore.z, midbefore.w);\n vec4 midafterr = vec4(midafter.x+rad,midafter.y, midafter.z, midafter.w);\n\n vec4 mb = projectionMatrix*midbefore;\n vec4 mbr = projectionMatrix*midbeforer;\n vec4 ma = projectionMatrix*midafter;\n vec4 mar = projectionMatrix*midafterr;\n mb /= mb.w;\n mbr /= mbr.w;\n ma /= ma.w;\n mar /= mar.w;\n scale = abs((mbr.x-mb.x)/(mar.x-ma.x));\n rad *= scale;\n r = abs(rad);\n }\n vec4 mvPosition = pt;\n p1 = pt.xyz; p2 = to.xyz;\n vec3 norm = to.xyz-pt.xyz;\n float mult = 1.1; //slop to account for perspective of sphere\n if(length(p1) > length(p2)) { //billboard at level of closest point\n mvPosition = to;\n }\n\n vec3 n = normalize(mvPosition.xyz);\n//intersect with the plane defined by the camera looking at the billboard point\n if(color.z >= 0.0) { //p1\n vec3 pnorm = normalize(p1);\n float t = dot(mvPosition.xyz-p1,n)/dot(pnorm,n);\n mvPosition.xyz = p1+t*pnorm;\n } else {\n vec3 pnorm = normalize(p2);\n float t = dot(mvPosition.xyz-p2,n)/dot(pnorm,n);\n mvPosition.xyz = p2+t*pnorm;\n mult *= -1.0;\n }\n\n if(outlineMaxPixels > 0.0) {\n vec4 cpos = mvPosition;\n vec4 unadjusted = projectionMatrix*vec4(cpos.x+abs(scale*radius), cpos.y,cpos.z,cpos.w); \n vec4 ccoord = projectionMatrix*cpos;\n vec4 adjust = projectionMatrix*vec4(cpos.x+r,cpos.y,cpos.z,cpos.w); \n unadjusted /= unadjusted.w;\n adjust /= adjust.w;\n unadjusted.xyz -= ccoord.xyz/ccoord.w;\n adjust.xyz -= ccoord.xyz/ccoord.w;\n float diff = abs(adjust.x-unadjusted.x);\n diff *= vWidth; //this should now be in pixels\n if(diff > outlineMaxPixels) {\n float fixlen = abs(unadjusted.x) + outlineMaxPixels/vWidth; \n vec4 pcoord = ccoord;\n pcoord.x += fixlen*pcoord.w;\n vec4 altc = projinv*pcoord;\n r= abs(altc.x-cpos.x);\n }\n }\n\n vec3 cr = normalize(cross(mvPosition.xyz,norm))*rad;\n vec3 doublecr = normalize(cross(mvPosition.xyz,cr))*rad;\n mvPosition.xy += mult*(cr + doublecr).xy;\n cposition = mvPosition.xyz;\n gl_Position = projectionMatrix * mvPosition;\n vLight = vec3(1.0,1.0,1.0);\n}\n\n",uniforms:{opacity:{type:"f",value:1},fogColor:{type:"c",value:new b.Color(1,1,1)},fogNear:{type:"f",value:1},fogFar:{type:"f",value:2e3},outlineColor:{type:"c",value:new b.Color(0,0,0)},outlineWidth:{type:"f",value:.1},outlinePushback:{type:"f",value:1},outlineMaxPixels:{type:"f",value:0},projinv:{type:"mat4",value:[]}}},lt={opacity:{type:"f",value:1},fogColor:{type:"c",value:new b.Color(1,1,1)},fogNear:{type:"f",value:1},fogFar:{type:"f",value:2e3},data:{type:"i",value:3},colormap:{type:"i",value:4},depthmap:{type:"i",value:5},step:{type:"f",value:1},maxdepth:{type:"f",value:100},subsamples:{type:"f",value:5},textmat:{type:"mat4",value:[]},projinv:{type:"mat4",value:[]},transfermin:{type:"f",value:-.2},transfermax:{type:"f",value:.2}},ht={fragmentShader:"\nuniform highp sampler3D data;\nuniform highp sampler2D colormap;\nuniform highp sampler2D depthmap;\n\n\nuniform mat4 textmat;\nuniform mat4 projinv;\nuniform mat4 projectionMatrix;\n\nuniform float step;\nuniform float subsamples;\nuniform float maxdepth;\nuniform float transfermin;\nuniform float transfermax;\nin vec4 mvPosition;\nout vec4 color;\nvoid main(void) {\n\n vec4 pos = mvPosition;\n bool seengood = false;\n float i = 0.0;\n color = vec4(1,1,1,0);\n float increment = 1.0/subsamples;\n float maxsteps = (maxdepth*subsamples/step);\n//there's probably a better way to do this..\n//calculate farthest possible point in model coordinates\n vec4 maxpos = vec4(pos.x,pos.y,pos.z-maxdepth,1.0);\n// convert to projection\n maxpos = projectionMatrix*maxpos;\n vec4 startp = projectionMatrix*pos;\n// homogonize\n maxpos /= maxpos.w;\n startp /= startp.w;\n//take x,y from start and z from max\n maxpos = vec4(startp.x,startp.y,maxpos.z,1.0);\n//convert back to model space\n maxpos = projinv*maxpos;\n maxpos /= maxpos.w;\n float incr = step/subsamples;\n//get depth from depthmap\n//startp is apparently [-1,1]\n vec2 tpos = startp.xy/2.0+0.5;\n float depth = texture(depthmap, tpos).r;\n//compute vector between start and end\n vec4 direction = maxpos-pos;\n for( i = 0.0; i <= maxsteps; i++) {\n vec4 pt = (pos+(i/maxsteps)*direction);\n vec4 ppt = projectionMatrix*pt;\n float ptdepth = ppt.z/ppt.w;\n ptdepth = ((gl_DepthRange.diff * ptdepth) + gl_DepthRange.near + gl_DepthRange.far) / 2.0;\n if(ptdepth > depth) break;\n pt = textmat*pt;\n// pt /= pt.w;\n if(pt.x >= -0.01 && pt.y >= -0.01 && pt.z >= -0.01 && pt.x <= 1.01 && pt.y <= 1.01 && pt.z <= 1.01) {\n seengood = true;\n } else if(seengood) {\n break;\n }\n if( pt.x < -0.01 || pt.x > 1.01 || pt.y < -0.01 || pt.y > 1.01 || pt.z < -0.01 || pt.z > 1.01 ){\n color.a = 0.0;\n continue;\n }\n else {\n float val = texture(data, pt.zyx).r;\n if(isinf(val)) continue; //masked out\n float cval = (val-transfermin)/(transfermax-transfermin); //scale to texture 0-1 range\n vec4 val_color = texture(colormap, vec2(cval,0.5));\n color.rgb = color.rgb*color.a + (1.0-color.a)*val_color.a*val_color.rgb;\n color.a += (1.0 - color.a) * val_color.a; \n if(color.a > 0.0) color.rgb /= color.a;\n// color = vec4(pt.x, pt.y, pt.z, 1.0);\n }\n// color = vec4(pt.x, pt.y, pt.z, 0.0)\n }\n}\n\n ".replace("#define GLSLIFY 1",""),vertexShader:"uniform mat4 modelViewMatrix;\nuniform mat4 projectionMatrix;\nuniform mat4 viewMatrix;\n\nin vec3 position;\nout vec4 mvPosition;\nvoid main() {\n\n mvPosition = modelViewMatrix * vec4( position, 1.0 );\n gl_Position = projectionMatrix*mvPosition;\n}\n".replace("#define GLSLIFY 1",""),uniforms:lt},ct={basic:V,blur:{fragmentShader:"const float INV_TOTAL_SAMPLES_FACTOR = 1.0 / 9.0;\nuniform highp sampler2D inTex;\nvarying highp vec2 vTexCoords;\n \nvoid main() {\n \n vec2 texelSize = 1.0 / vec2(textureSize(inTex,0));\n float blurred_visibility_factor = 0.0f;\n for (int t = -1; t <= 1; ++t) {\n for (int s = -1; s <= 1; ++s) {\n vec2 offset = vec2(float(s), float(t)) * texelSize;\n blurred_visibility_factor += texture2D(inTex, vTexCoords + offset).r;\n }\n }\n \n gl_FragDepthEXT = blurred_visibility_factor * INV_TOTAL_SAMPLES_FACTOR;\n \n}",vertexShader:"attribute vec2 vertexPosition;\nvarying highp vec2 vTexCoords;\nconst vec2 scale = vec2(0.5, 0.5);\n\nvoid main() {\n vTexCoords = vertexPosition * scale + scale; // scale vertex attribute to [0,1] range\n gl_Position = vec4(vertexPosition, 0.0, 1.0);\n}\n ",uniforms:{}},instanced:H,lambert:q,lambertdouble:Z,outline:K,screen:Q,screenaa:$,ssao:{fragmentShader:"uniform sampler2D depthmap;\nvarying highp vec2 vTexCoords;\nuniform float vWidth;\nuniform float vHeight;\nuniform float total_strength;\nuniform float radius;\nuniform mat4 projinv;\nuniform mat4 projectionMatrix;\n\n//code for pseudorandom vector from chatgpt\nfloat hash(vec3 p) {\n p = fract(p * vec3(0.1031, 0.1030, 0.0973));\n p += dot(p, p.yzx + 33.33);\n return fract((p.x + p.y) * p.z);\n}\n\n// Generate a pseudorandom vec3 from a seed vec3\nvec3 pseudorandomVec3(vec3 seed) {\n vec3 randomVec;\n randomVec.x = hash(seed);\n randomVec.y = hash(seed + vec3(1.0, 0.0, 17.1));\n randomVec.z = hash(seed + vec3(0.0, 13.23, 0.0));\n return randomVec;\n}\n\nvoid main(void)\n{ \n const float base = 0.2;\n const float area = 0.75;\n const int cycles = 1;\n\n const int samples = 64;\n vec3 sample_sphere[64] = vec3[](\n vec3(0.091258,-0.510164,0.000000),\n vec3(-0.204347,-0.872967,0.187199),\n vec3(0.009690,-0.263696,-0.110414),\n vec3(0.175208,-0.563987,0.228527),\n vec3(-0.001824,-0.003113,-0.000323),\n vec3(0.411134,-0.719869,-0.261530),\n vec3(-0.074272,-0.377368,0.276290),\n vec3(-0.147773,-0.381587,-0.284529),\n vec3(0.173317,-0.199635,0.063295),\n vec3(-0.186452,-0.199460,0.076965),\n vec3(0.143985,-0.308160,-0.307687),\n vec3(0.053194,-0.148286,0.169589),\n vec3(-0.547656,-0.486476,-0.317378),\n vec3(0.020804,-0.015092,-0.004574),\n vec3(-0.038006,-0.043165,0.054059),\n vec3(-0.094795,-0.443908,-0.731525),\n vec3(0.547552,-0.396466,0.461477),\n vec3(-0.176886,-0.089989,0.007315),\n vec3(0.074401,-0.048840,-0.074039),\n vec3(-0.008240,-0.075697,0.178197),\n vec3(-0.307880,-0.185053,-0.368943),\n vec3(0.309520,-0.108483,0.041646),\n vec3(-0.773478,-0.292946,0.538166),\n vec3(0.184487,-0.231594,-0.820065),\n vec3(0.207318,-0.100531,0.361797),\n vec3(-0.173306,-0.037737,-0.055289),\n vec3(0.548102,-0.105342,-0.253237),\n vec3(-0.119342,-0.043907,0.285162),\n vec3(-0.270247,-0.087861,-0.751357),\n vec3(0.449312,-0.039777,0.236146),\n vec3(-0.743773,-0.036095,0.196056),\n vec3(0.148819,-0.004300,-0.231448),\n vec3(0.008773,0.000809,0.051047),\n vec3(-0.461467,0.027390,-0.357386),\n vec3(0.169626,0.013338,-0.014053),\n vec3(-0.043786,0.007095,0.047331),\n vec3(0.004821,0.140371,-0.988260),\n vec3(0.092402,0.023994,0.101860),\n vec3(-0.295335,0.061530,-0.027372),\n vec3(0.024903,0.007537,-0.018901),\n vec3(-0.081463,0.125402,0.447794),\n vec3(-0.397119,0.231805,-0.631062),\n vec3(0.163853,0.059014,0.044905),\n vec3(-0.495220,0.214357,0.254139),\n vec3(0.306123,0.373687,-0.825715),\n vec3(0.021665,0.026737,0.053220),\n vec3(-0.208231,0.117129,-0.098685),\n vec3(0.139749,0.080968,-0.043086),\n vec3(-0.153599,0.182814,0.262090),\n vec3(-0.159673,0.496777,-0.743568),\n vec3(0.134797,0.117152,0.095753),\n vec3(-0.155626,0.120533,0.019395),\n vec3(0.042311,0.054462,-0.049709),\n vec3(0.001257,0.031288,0.034468),\n vec3(-0.002271,0.003199,-0.002304),\n vec3(0.662104,0.717307,0.033854),\n vec3(-0.373100,0.576021,0.308274),\n vec3(0.024233,0.231316,-0.173688),\n vec3(0.161311,0.420217,0.234273),\n vec3(-0.045248,0.078031,-0.010411),\n vec3(0.167453,0.376942,-0.094872),\n vec3(-0.056194,0.433247,0.173218),\n vec3(-0.016224,0.123149,-0.035569),\n vec3(0.067127,0.407641,0.028479)\n );\n\n float depth = texture2D(depthmap, vTexCoords).r;\n if(depth == 1.0) {\n discard;\n }\n\n vec4 screen_position = vec4(vTexCoords, depth,1.0);\n vec4 pos = projinv*screen_position;\n pos /= pos.w;\n vec3 position = pos.xyz;\n\n //approximate the normal from the depth map; I find this simpler\n //than trying to recompute the exact normal within every possible object shader\n\n //pixel offset positions in screen space\n ivec2 dim = textureSize(depthmap,0);\n vec2 offset1 = vec2(0.0,1.0/float(dim.y));\n vec2 offset2 = vec2(1.0/float(dim.x),0.0);\n float depth1 = texture2D(depthmap, vTexCoords + offset1).r;\n float depth2 = texture2D(depthmap, vTexCoords + offset2).r;\n \n vec3 p1 = vec3(screen_position.xy+offset1, depth1 - depth);\n vec3 p2 = vec3(screen_position.xy+offset2, depth2 - depth);\n\n //convert to model space\n vec4 pos1 = projinv*vec4(p1,1);\n pos1 /= pos1.w;\n vec4 pos2 = projinv*vec4(p2,1);\n pos2 /= pos2.w;\n\n vec3 normal = normalize(cross(pos1.xyz-position, pos2.xyz-position)); //model normal, important we normalize in model space\n\n //pseudo random number\n\n float occlusion = 0.0;\n for(int c = 0; c < cycles; c++) {\n vec3 random = normalize(pseudorandomVec3(position+float(c)));\n for(int i=0; i < samples; i++) {\n\n vec3 ray = radius * reflect(sample_sphere[i],random);\n vec3 hemi_ray = position + sign(dot(ray,normal)) * ray; //model space\n vec4 hemi_screen = projectionMatrix*vec4(hemi_ray,1.0);\n hemi_screen /= hemi_screen.w;\n \n float occ_depth = texture2D(depthmap, clamp(hemi_screen.xy,0.0,1.0)).r;\n float difference = hemi_screen.z - occ_depth;\n \n occlusion += step(0.0, difference) * (1.0-smoothstep(0.0, area, difference));\n }\n }\n float ao = 1.0 - total_strength * occlusion * (1.0 / float(cycles*samples));\n gl_FragDepthEXT = clamp(ao+base,0.0,1.0);\n\n}",vertexShader:"attribute vec2 vertexPosition;\nvarying highp vec2 vTexCoords;\nconst vec2 scale = vec2(0.5, 0.5);\n\nvoid main() {\n vTexCoords = vertexPosition * scale + scale; // scale vertex attribute to [0,1] range\n gl_Position = vec4(vertexPosition, 0.0, 1.0);\n}\n ",uniforms:{total_strength:{type:"f",value:1},radius:{type:"f",value:5},projinv:{type:"mat4",value:[]}}},sphereimposter:tt,sphereimposteroutline:it,sprite:rt,stickimposter:at,stickimposteroutline:ot,volumetric:ht};function dt(t){let e={};for(const r in t){e[r]={},e[r].type=t[r].type;var i=t[r].value;i instanceof b.Color?e[r].value=i.clone():"number"==typeof i?e[r].value=i:i instanceof Array?e[r].value=[]:console.error("Error copying shader uniforms from ShaderLib: unknown type for uniform")}return e}const ut={clone:dt};class Camera extends Object3D{constructor(t=50,e=1,i=.1,r=2e3,s=!1){super(),this.projectionMatrix=new l.Matrix4,this.projectionMatrixInverse=new l.Matrix4,this.matrixWorldInverse=new l.Matrix4,this.fov=t,this.aspect=e,this.near=i,this.far=r;var n=this.position.z;this.right=n*Math.tan(Math.PI/180*t),this.left=-this.right,this.top=this.right/this.aspect,this.bottom=-this.top,this.ortho=!!s,this.updateProjectionMatrix()}lookAt(t){this.matrix.lookAt(this.position,t,this.up),this.rotationAutoUpdate&&(!1===this.useQuaternion&&this.rotation instanceof l.Vector3?this.rotation.setEulerFromRotationMatrix(this.matrix,this.eulerOrder):console.error("Unimplemented math operation."))}updateProjectionMatrix(){this.ortho?this.projectionMatrix.makeOrthographic(this.left,this.right,this.top,this.bottom,this.near,this.far):this.projectionMatrix.makePerspective(this.fov,this.aspect,this.near,this.far),this.projectionMatrixInverse.getInverse(this.projectionMatrix)}}class Fog{constructor(t,e=1,i=1e3){this.name="",this.color=new b.Color(t),this.near=e,this.far=i}clone(){return new Fog(this.color.getHex(),this.near,this.far)}}class SpritePlugin{constructor(){this.sprite={vertices:null,faces:null,vertexBuffer:null,elementBuffer:null,program:null,attributes:{},uniforms:null}}init(t){this.gl=t.context,this.renderer=t,this.precision=t.getPrecision(),this.sprite.vertices=new Float32Array(16),this.sprite.faces=new Uint16Array(6);var e=0;this.sprite.vertices[e++]=-1,this.sprite.vertices[e++]=-1,this.sprite.vertices[e++]=0,this.sprite.vertices[e++]=0,this.sprite.vertices[e++]=1,this.sprite.vertices[e++]=-1,this.sprite.vertices[e++]=1,this.sprite.vertices[e++]=0,this.sprite.vertices[e++]=1,this.sprite.vertices[e++]=1,this.sprite.vertices[e++]=1,this.sprite.vertices[e++]=1,this.sprite.vertices[e++]=-1,this.sprite.vertices[e++]=1,this.sprite.vertices[e++]=0,this.sprite.vertices[e++]=1,e=0,this.sprite.faces[e++]=0,this.sprite.faces[e++]=1,this.sprite.faces[e++]=2,this.sprite.faces[e++]=0,this.sprite.faces[e++]=2,this.sprite.faces[e++]=3,this.sprite.vertexBuffer=this.gl.createBuffer(),this.sprite.elementBuffer=this.gl.createBuffer(),this.gl.bindBuffer(this.gl.ARRAY_BUFFER,this.sprite.vertexBuffer),this.gl.bufferData(this.gl.ARRAY_BUFFER,this.sprite.vertices,this.gl.STATIC_DRAW),this.gl.bindBuffer(this.gl.ELEMENT_ARRAY_BUFFER,this.sprite.elementBuffer),this.gl.bufferData(this.gl.ELEMENT_ARRAY_BUFFER,this.sprite.faces,this.gl.STATIC_DRAW),this.sprite.program=this.createProgram(ct.sprite,this.precision||1),this.sprite.attributes={};const i={};this.sprite.attributes.position=this.gl.getAttribLocation(this.sprite.program,"position"),this.sprite.attributes.uv=this.gl.getAttribLocation(this.sprite.program,"uv"),i.uvOffset=this.gl.getUniformLocation(this.sprite.program,"uvOffset"),i.uvScale=this.gl.getUniformLocation(this.sprite.program,"uvScale"),i.rotation=this.gl.getUniformLocation(this.sprite.program,"rotation"),i.scale=this.gl.getUniformLocation(this.sprite.program,"scale"),i.alignment=this.gl.getUniformLocation(this.sprite.program,"alignment"),i.color=this.gl.getUniformLocation(this.sprite.program,"color"),i.map=this.gl.getUniformLocation(this.sprite.program,"map"),i.opacity=this.gl.getUniformLocation(this.sprite.program,"opacity"),i.useScreenCoordinates=this.gl.getUniformLocation(this.sprite.program,"useScreenCoordinates"),i.screenPosition=this.gl.getUniformLocation(this.sprite.program,"screenPosition"),i.modelViewMatrix=this.gl.getUniformLocation(this.sprite.program,"modelViewMatrix"),i.projectionMatrix=this.gl.getUniformLocation(this.sprite.program,"projectionMatrix"),i.fogType=this.gl.getUniformLocation(this.sprite.program,"fogType"),i.fogDensity=this.gl.getUniformLocation(this.sprite.program,"fogDensity"),i.fogNear=this.gl.getUniformLocation(this.sprite.program,"fogNear"),i.fogFar=this.gl.getUniformLocation(this.sprite.program,"fogFar"),i.fogColor=this.gl.getUniformLocation(this.sprite.program,"fogColor"),i.alphaTest=this.gl.getUniformLocation(this.sprite.program,"alphaTest"),this.sprite.uniforms=i}render(t,e,i,r,s){var n,a,o,l,h,c,d,u,f,p;if(!this.gl)throw new Error("WebGLRenderer not initialized");let g=[];null===(n=null==t?void 0:t.__webglSprites)||void 0===n||n.forEach((t=>{(s&&0==t.material.depthTest||!s&&t.material.depthTest)&&g.push(t)}));let m=g.length;if(!m)return;const v=this.sprite.attributes,_=this.sprite.uniforms;if(!_)throw new Error("Uniforms not defined");var y=.5*i,b=.5*r;this.gl.useProgram(this.sprite.program),this.gl.enableVertexAttribArray(v.position),this.gl.enableVertexAttribArray(v.uv),this.gl.disable(this.gl.CULL_FACE),this.gl.enable(this.gl.BLEND),this.gl.bindBuffer(this.gl.ARRAY_BUFFER,this.sprite.vertexBuffer),this.gl.vertexAttribPointer(v.position,2,this.gl.FLOAT,!1,16,0),this.gl.vertexAttribPointer(v.uv,2,this.gl.FLOAT,!1,16,8),this.gl.bindBuffer(this.gl.ELEMENT_ARRAY_BUFFER,this.sprite.elementBuffer),this.gl.uniformMatrix4fv(_.projectionMatrix,!1,e.projectionMatrix.elements),this.gl.activeTexture(this.gl.TEXTURE0),this.gl.uniform1i(_.map,0);var x,w=0,A=0,C=t.fog;let S,M,z,T;C?(this.gl.uniform3f(_.fogColor,C.color.r,C.color.g,C.color.b),this.gl.uniform1f(_.fogNear,C.near),this.gl.uniform1f(_.fogFar,C.far),this.gl.uniform1i(_.fogType,1),w=1,A=1):(this.gl.uniform1i(_.fogType,0),w=0,A=0);let E=[];for(x=0;x0}disableAmbientOcclusion(){this._AOEnabled=!1}setViewport(){if(this._offscreen&&(this._offscreen.width=this._canvas.width,this._offscreen.height=this._canvas.height),null!=this.rows&&null!=this.cols&&null!=this.row&&null!=this.col){var t=this._canvas.width/this.cols,e=this._canvas.height/this.rows;this._viewportWidth=t,this._viewportHeight=e,this.isLost()||(this._gl.enable(this._gl.SCISSOR_TEST),this._gl.scissor(t*this.col,e*this.row,t,e),this._gl.viewport(t*this.col,e*this.row,t,e))}}setSize(t,e){if(this.devicePixelRatio=void 0!==window.devicePixelRatio?window.devicePixelRatio:1,this._antialias&&this.devicePixelRatio<2&&(this.devicePixelRatio*=2),null!=this.rows&&null!=this.cols&&null!=this.row&&null!=this.col){var i=t/this.cols,r=e/this.rows;this._canvas.width=t*this.devicePixelRatio,this._canvas.height=e*this.devicePixelRatio,this._viewportWidth=i*this.devicePixelRatio,this._viewportHeight=r*this.devicePixelRatio,this._canvas.style.width=t+"px",this._canvas.style.height=e+"px",this.setViewport()}else this._viewportWidth=this._canvas.width=t*this.devicePixelRatio,this._viewportHeight=this._canvas.height=e*this.devicePixelRatio,this._canvas.style.width=t+"px",this._canvas.style.height=e+"px",this.isLost()||this._gl.viewport(0,0,this._gl.drawingBufferWidth,this._gl.drawingBufferHeight);this.initFrameBuffer()}clear(t,e,i){var r=0;(void 0===t||t)&&(r|=this._gl.COLOR_BUFFER_BIT),(void 0===e||e)&&(r|=this._gl.DEPTH_BUFFER_BIT),(void 0===i||i)&&(r|=this._gl.STENCIL_BUFFER_BIT),this._gl.clear(r)}setMaterialFaces(t,e){var i=t.side===a,r=t.side===n;t.imposter||(r=e?!r:r),this._oldDoubleSided!==i&&(i?this._gl.disable(this._gl.CULL_FACE):this._gl.enable(this._gl.CULL_FACE),this._oldDoubleSided=i),this._oldFlipSided!==r&&(r?this._gl.frontFace(this._gl.CW):this._gl.frontFace(this._gl.CCW),this._oldFlipSided=r),this._gl.cullFace(this._gl.BACK)}setDepthTest(t){this._oldDepthTest!==t&&(t?this._gl.enable(this._gl.DEPTH_TEST):this._gl.disable(this._gl.DEPTH_TEST),this._oldDepthTest=t)}setDepthWrite(t){this._oldDepthWrite!==t&&(this._gl.depthMask(t),this._oldDepthWrite=t)}setBlending(t){t?(this._gl.enable(this._gl.BLEND),this._gl.blendEquationSeparate(this._gl.FUNC_ADD,this._gl.FUNC_ADD),this._gl.blendFuncSeparate(this._gl.SRC_ALPHA,this._gl.ONE_MINUS_SRC_ALPHA,this._gl.ONE,this._gl.ONE_MINUS_SRC_ALPHA)):this._gl.disable(this._gl.BLEND)}initMaterial(t,e,i,r){var s,n;if(t.addEventListener("dispose",this.onMaterialDispose.bind(this)),n=t.shaderID){var a=ct[n];t.vertexShader=a.vertexShader,t.fragmentShader=a.fragmentShader,t.uniforms=ut.clone(a.uniforms),t.shaded&&t.makeShaded(this.SHADE_TEXTURE)}s={wireframe:t.wireframe,fragdepth:t.imposter,volumetric:t.volumetric,shaded:t.shaded},t.program=this.buildProgram(t.fragmentShader,t.vertexShader,t.uniforms,s)}renderBuffer(t,e,i,r,s,n){var a,o;if(r.visible&&(a=this.setProgram(t,e,i,r,n,this))){o=a.attributes;var l,h,c=!1,d=r.wireframe?1:0,u=16777215*s.id+2*a.id+d;if(u!==this._currentGeometryGroupHash&&(this._currentGeometryGroupHash=u,c=!0),c&&(this.disableAttributes(),o.position>=0&&(this._gl.bindBuffer(this._gl.ARRAY_BUFFER,s.__webglVertexBuffer),this.enableAttribute(o.position),this._gl.vertexAttribPointer(o.position,3,this._gl.FLOAT,!1,0,0)),o.color>=0&&(this._gl.bindBuffer(this._gl.ARRAY_BUFFER,s.__webglColorBuffer),this.enableAttribute(o.color),this._gl.vertexAttribPointer(o.color,3,this._gl.FLOAT,!1,0,0)),o.normal>=0&&(this._gl.bindBuffer(this._gl.ARRAY_BUFFER,s.__webglNormalBuffer),this.enableAttribute(o.normal),this._gl.vertexAttribPointer(o.normal,3,this._gl.FLOAT,!1,0,0)),o.offset>=0&&(this._gl.bindBuffer(this._gl.ARRAY_BUFFER,s.__webglOffsetBuffer),this.enableAttribute(o.offset),this._gl.vertexAttribPointer(o.offset,3,this._gl.FLOAT,!1,0,0)),o.radius>=0&&(this._gl.bindBuffer(this._gl.ARRAY_BUFFER,s.__webglRadiusBuffer),this.enableAttribute(o.radius),this._gl.vertexAttribPointer(o.radius,1,this._gl.FLOAT,!1,0,0))),n instanceof Mesh){if("instanced"===r.shaderID){var f=r.sphere.geometryGroups[0];c&&(this._gl.bindBuffer(this._gl.ARRAY_BUFFER,s.__webglVertexBuffer),this._gl.bufferData(this._gl.ARRAY_BUFFER,f.vertexArray,this._gl.STATIC_DRAW),this._gl.bindBuffer(this._gl.ARRAY_BUFFER,s.__webglNormalBuffer),this._gl.bufferData(this._gl.ARRAY_BUFFER,f.normalArray,this._gl.STATIC_DRAW),this._gl.bindBuffer(this._gl.ELEMENT_ARRAY_BUFFER,s.__webglFaceBuffer),this._gl.bufferData(this._gl.ELEMENT_ARRAY_BUFFER,f.faceArray,this._gl.STATIC_DRAW)),l=f.faceidx,this._extInstanced.vertexAttribDivisorANGLE(o.offset,1),this._extInstanced.vertexAttribDivisorANGLE(o.radius,1),this._extInstanced.vertexAttribDivisorANGLE(o.color,1),this._extInstanced.drawElementsInstancedANGLE(this._gl.TRIANGLES,l,this._gl.UNSIGNED_SHORT,0,s.radiusArray.length),this._extInstanced.vertexAttribDivisorANGLE(o.offset,0),this._extInstanced.vertexAttribDivisorANGLE(o.radius,0),this._extInstanced.vertexAttribDivisorANGLE(o.color,0)}else r.wireframe?(h=s.lineidx,this.setLineWidth(r.wireframeLinewidth),c&&this._gl.bindBuffer(this._gl.ELEMENT_ARRAY_BUFFER,s.__webglLineBuffer),this._gl.drawElements(this._gl.LINES,h,this._gl.UNSIGNED_SHORT,0)):(l=s.faceidx,c&&this._gl.bindBuffer(this._gl.ELEMENT_ARRAY_BUFFER,s.__webglFaceBuffer),this._gl.drawElements(this._gl.TRIANGLES,l,this._gl.UNSIGNED_SHORT,0));this.info.render.calls++,this.info.render.vertices+=l,this.info.render.faces+=l/3}else n instanceof Line&&(h=s.vertices,this.setLineWidth(r.linewidth),this._gl.drawArrays(this._gl.LINES,0,h),this.info.render.calls++)}}clearShading(){this._gl.framebufferTexture2D(this._gl.FRAMEBUFFER,this._gl.DEPTH_ATTACHMENT,this._gl.TEXTURE_2D,this._shadingTexture,0),this.clear(!1,!0,!1),this._gl.framebufferTexture2D(this._gl.FRAMEBUFFER,this._gl.DEPTH_ATTACHMENT,this._gl.TEXTURE_2D,this._depthTexture,0)}setShading(t,e,i){let r=t.__lights,s=t.fog,n=[];for(let e=0,r=t.__webglObjects.length;e=0;--i)t[i].object===e&&t.splice(i,1)}removeInstancesDirect(t,e){for(var i=t.length-1;i>=0;--i)t[i]===e&&t.splice(i,1)}unrollBufferMaterial(t){var e=t.object.material;if(e.volumetric)t.opaque=null,t.transparent=null,t.volumetric=e;else if(e.transparent){if(t.opaque=null,t.volumetric=null,t.transparent=e,!e.wireframe){let i=e.clone();i.opacity=0,t.transparentDepth=i}}else{if(t.opaque=e,t.transparent=null,t.volumetric=null,!e.wireframe){let i=e.clone();i.opacity=0,t.opaqueDepth=i}e.hasAO&&(t.hasAO=!0),(this._AOEnabled||t.hasAO)&&(t.opaqueShaded=e.clone(),t.opaqueShaded.shaded=!0)}}setBuffers(t,e){var i=t.vertexArray,r=t.colorArray;if(void 0!==t.__webglOffsetBuffer?(this._gl.bindBuffer(this._gl.ARRAY_BUFFER,t.__webglOffsetBuffer),this._gl.bufferData(this._gl.ARRAY_BUFFER,i,e)):(this._gl.bindBuffer(this._gl.ARRAY_BUFFER,t.__webglVertexBuffer),this._gl.bufferData(this._gl.ARRAY_BUFFER,i,e)),this._gl.bindBuffer(this._gl.ARRAY_BUFFER,t.__webglColorBuffer),this._gl.bufferData(this._gl.ARRAY_BUFFER,r,e),t.normalArray&&void 0!==t.__webglNormalBuffer){var s=t.normalArray;this._gl.bindBuffer(this._gl.ARRAY_BUFFER,t.__webglNormalBuffer),this._gl.bufferData(this._gl.ARRAY_BUFFER,s,e)}if(t.radiusArray&&void 0!==t.__webglRadiusBuffer&&(this._gl.bindBuffer(this._gl.ARRAY_BUFFER,t.__webglRadiusBuffer),this._gl.bufferData(this._gl.ARRAY_BUFFER,t.radiusArray,e)),t.faceArray&&void 0!==t.__webglFaceBuffer){var n=t.faceArray;this._gl.bindBuffer(this._gl.ELEMENT_ARRAY_BUFFER,t.__webglFaceBuffer),this._gl.bufferData(this._gl.ELEMENT_ARRAY_BUFFER,n,e)}if(t.lineArray&&void 0!==t.__webglLineBuffer){var a=t.lineArray;this._gl.bindBuffer(this._gl.ELEMENT_ARRAY_BUFFER,t.__webglLineBuffer),this._gl.bufferData(this._gl.ELEMENT_ARRAY_BUFFER,a,e)}}createMeshBuffers(t){t.radiusArray&&(t.__webglRadiusBuffer=this._gl.createBuffer()),t.useOffset&&(t.__webglOffsetBuffer=this._gl.createBuffer()),t.__webglVertexBuffer=this._gl.createBuffer(),t.__webglNormalBuffer=this._gl.createBuffer(),t.__webglColorBuffer=this._gl.createBuffer(),t.__webglFaceBuffer=this._gl.createBuffer(),t.__webglLineBuffer=this._gl.createBuffer(),this.info.memory.geometries++}createLineBuffers(t){t.__webglVertexBuffer=this._gl.createBuffer(),t.__webglColorBuffer=this._gl.createBuffer(),this.info.memory.geometries++}addBuffer(t,e,i){t.push({buffer:e,object:i,opaque:null,transparent:null})}setupMatrices(t,e){t._modelViewMatrix.multiplyMatrices(e.matrixWorldInverse,t.matrixWorld),t._normalMatrix.getInverse(t._modelViewMatrix),t._normalMatrix.transpose()}filterFallback(t){return this._gl.LINEAR}setTextureParameters(t,e){t==this._gl.TEXTURE_2D?(this._gl.texParameteri(t,this._gl.TEXTURE_WRAP_S,this._gl.CLAMP_TO_EDGE),this._gl.texParameteri(t,this._gl.TEXTURE_WRAP_T,this._gl.CLAMP_TO_EDGE),this._gl.texParameteri(t,this._gl.TEXTURE_MAG_FILTER,this.filterFallback(e.magFilter)),this._gl.texParameteri(t,this._gl.TEXTURE_MIN_FILTER,this.filterFallback(e.minFilter))):(this._gl.texParameteri(t,this._gl.TEXTURE_WRAP_S,this._gl.CLAMP_TO_EDGE),this._gl.texParameteri(t,this._gl.TEXTURE_WRAP_T,this._gl.CLAMP_TO_EDGE),this._gl.texParameteri(t,this._gl.TEXTURE_WRAP_R,this._gl.CLAMP_TO_EDGE),this._extColorBufferFloat&&this._extFloatLinear?(this._gl.texParameteri(t,this._gl.TEXTURE_MAG_FILTER,this._gl.LINEAR),this._gl.texParameteri(t,this._gl.TEXTURE_MIN_FILTER,this._gl.LINEAR)):(this._gl.texParameteri(t,this._gl.TEXTURE_MAG_FILTER,this._gl.NEAREST),this._gl.texParameteri(t,this._gl.TEXTURE_MIN_FILTER,this._gl.NEAREST)))}paramToGL(t){return t===p?this._gl.UNSIGNED_BYTE:t===m?this._gl.RGBA:t===u?this._gl.NEAREST:0}setupLights(t,e){var i,r,s,n,a,o=this._lights,l=o.directional.colors,h=o.directional.positions,c=0,d=0;for(i=0,r=e.length;i{"use strict";i.r(e),i.d(e,{Matrix3:()=>Matrix3,Matrix4:()=>Matrix4,Quaternion:()=>Quaternion,Ray:()=>Ray,Vector2:()=>Vector2,Vector3:()=>Vector3,clamp:()=>r,conversionMatrix3:()=>d,degToRad:()=>n});class Quaternion{constructor(t,e,i,r){this.x=t||0,this.y=e||0,this.z=i||0,this.w=void 0!==r?r:1}set(t,e,i,r){return this.x=t,this.y=e,this.z=i,this.w=r,this}copy(t){return this.x=t.x,this.y=t.y,this.z=t.z,this.w=t.w,this}conjugate(){return this.x*=-1,this.y*=-1,this.z*=-1,this}inverse(){return this.conjugate().normalize()}length(){return Math.sqrt(this.x*this.x+this.y*this.y+this.z*this.z+this.w*this.w)}lengthxyz(){return Math.sqrt(this.x*this.x+this.y*this.y+this.z*this.z)}normalize(){let t=this.length();return 0===t?(this.x=0,this.y=0,this.z=0,this.w=1):(t=1/t,this.x*=t,this.y*=t,this.z*=t,this.w*=t),this}multiply(t){return this.multiplyQuaternions(this,t)}multiplyScalar(t){return this.x*=t,this.y*=t,this.z*=t,this.w*=t,this}multiplyQuaternions(t,e){const i=t.x,r=t.y,s=t.z,n=t.w,a=e.x,o=e.y,l=e.z,h=e.w;return this.x=i*h+n*a+r*l-s*o,this.y=r*h+n*o+s*a-i*l,this.z=s*h+n*l+i*o-r*a,this.w=n*h-i*a-r*o-s*l,this}sub(t){return this.x-=t.x,this.y-=t.y,this.z-=t.z,this.w-=t.w,this}clone(){return new Quaternion(this.x,this.y,this.z,this.w)}setFromEuler(t){const e=Math.cos(t.x/2),i=Math.cos(t.y/2),r=Math.cos(t.z/2),s=Math.sin(t.x/2),n=Math.sin(t.y/2),a=Math.sin(t.z/2);return this.x=s*i*r+e*n*a,this.y=e*n*r-s*i*a,this.z=e*i*a+s*n*r,this.w=e*i*r-s*n*a,this}}class Vector2{constructor(t,e){this.x=t||0,this.y=e||0}set(t,e){return this.x=t,this.y=e,this}subVectors(t,e){return this.x=t.x-e.x,this.y=t.y-e.y,this}copy(t){return this.x=t.x,this.y=t.y,this}clone(){return new Vector2(this.x,this.y)}}function r(t,e,i){return Math.min(Math.max(t,e),i)}const s=Math.PI/180;function n(t){return t*s}var a,o,l,h,c;class Matrix4{constructor(t=1,e=0,i=0,r=0,s=0,n=1,a=0,o=0,l=0,h=0,c=1,d=0,u=0,f=0,p=0,g=1){void 0!==t&&"number"!=typeof t?this.elements=new Float32Array(t):(this.elements=new Float32Array(16),this.elements[0]=t,this.elements[4]=e,this.elements[8]=i,this.elements[12]=r,this.elements[1]=s,this.elements[5]=n,this.elements[9]=a,this.elements[13]=o,this.elements[2]=l,this.elements[6]=h,this.elements[10]=c,this.elements[14]=d,this.elements[3]=u,this.elements[7]=f,this.elements[11]=p,this.elements[15]=g)}makeScale(t,e,i){throw new Error("Method not implemented.")}set(t,e,i,r,s,n,a,o,l,h,c,d,u,f,p,g){const m=this.elements;return m[0]=t,m[4]=e,m[8]=i,m[12]=r,m[1]=s,m[5]=n,m[9]=a,m[13]=o,m[2]=l,m[6]=h,m[10]=c,m[14]=d,m[3]=u,m[7]=f,m[11]=p,m[15]=g,this}identity(){return this.set(1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1),this}copy(t){const e=t.elements;return this.set(e[0],e[4],e[8],e[12],e[1],e[5],e[9],e[13],e[2],e[6],e[10],e[14],e[3],e[7],e[11],e[15]),this}matrix3FromTopLeft(){const t=this.elements;return new Matrix3(t[0],t[4],t[8],t[1],t[5],t[9],t[2],t[6],t[10])}setRotationFromEuler(t,e){const i=this.elements,{x:r,y:s,z:n}=t,a=Math.cos(r),o=Math.sin(r),l=Math.cos(s),h=Math.sin(s),c=Math.cos(n),d=Math.sin(n);if(void 0===e||"XYZ"===e){const t=a*c,e=a*d,r=o*c,s=o*d;i[0]=l*c,i[4]=-l*d,i[8]=h,i[1]=e+r*h,i[5]=t-s*h,i[9]=-o*l,i[2]=s-t*h,i[6]=r+e*h,i[10]=a*l}else console.error(`Error with matrix4 setRotationFromEuler. Order: ${e}`);return this}setRotationFromQuaternion(t){const e=this.elements,{x:i,y:r,z:s,w:n}=t,a=i+i,o=r+r,l=s+s,h=i*a,c=i*o,d=i*l,u=r*o,f=r*l,p=s*l,g=n*a,m=n*o,v=n*l;return e[0]=1-(u+p),e[4]=c-v,e[8]=d+m,e[1]=c+v,e[5]=1-(h+p),e[9]=f-g,e[2]=d-m,e[6]=f+g,e[10]=1-(h+u),this}multiplyMatrices(t,e){const i=t.elements,r=e.elements,s=this.elements,n=i[0],a=i[4],o=i[8],l=i[12],h=i[1],c=i[5],d=i[9],u=i[13],f=i[2],p=i[6],g=i[10],m=i[14],v=i[3],_=i[7],y=i[11],b=i[15],x=r[0],w=r[4],A=r[8],C=r[12],S=r[1],M=r[5],z=r[9],T=r[13],E=r[2],L=r[6],F=r[10],I=r[14],O=r[3],D=r[7],k=r[11],R=r[15];return s[0]=n*x+a*S+o*E+l*O,s[4]=n*w+a*M+o*L+l*D,s[8]=n*A+a*z+o*F+l*k,s[12]=n*C+a*T+o*I+l*R,s[1]=h*x+c*S+d*E+u*O,s[5]=h*w+c*M+d*L+u*D,s[9]=h*A+c*z+d*F+u*k,s[13]=h*C+c*T+d*I+u*R,s[2]=f*x+p*S+g*E+m*O,s[6]=f*w+p*M+g*L+m*D,s[10]=f*A+p*z+g*F+m*k,s[14]=f*C+p*T+g*I+m*R,s[3]=v*x+_*S+y*E+b*O,s[7]=v*w+_*M+y*L+b*D,s[11]=v*A+_*z+y*F+b*k,s[15]=v*C+_*T+y*I+b*R,this}multiplyScalar(t){const e=this.elements;return e[0]*=t,e[4]*=t,e[8]*=t,e[12]*=t,e[1]*=t,e[5]*=t,e[9]*=t,e[13]*=t,e[2]*=t,e[6]*=t,e[10]*=t,e[14]*=t,e[3]*=t,e[7]*=t,e[11]*=t,e[15]*=t,this}makeTranslation(t,e,i){return this.set(1,0,0,t,0,1,0,e,0,0,1,i,0,0,0,1),this}snap(t){t||(t=4);const e=Math.pow(10,4),i=this.elements;for(let t=0;t<16;t++){const r=Math.round(i[t]);r===Math.round(i[t]*e)/e&&(i[t]=r)}return this}transpose(){const t=this.elements;let e;return e=t[1],t[1]=t[4],t[4]=e,e=t[2],t[2]=t[8],t[8]=e,e=t[6],t[6]=t[9],t[9]=e,e=t[3],t[3]=t[12],t[12]=e,e=t[7],t[7]=t[13],t[13]=e,e=t[11],t[11]=t[14],t[14]=e,this}setPosition(t){const e=this.elements;return e[12]=t.x,e[13]=t.y,e[14]=t.z,this}translate(t){const e=this.elements;return e[12]+=t.x,e[13]+=t.y,e[14]+=t.z,this}getInverse(t,e){const i=this.elements,r=t.elements,s=r[0],n=r[4],a=r[8],o=r[12],l=r[1],h=r[5],c=r[9],d=r[13],u=r[2],f=r[6],p=r[10],g=r[14],m=r[3],v=r[7],_=r[11],y=r[15];i[0]=c*g*v-d*p*v+d*f*_-h*g*_-c*f*y+h*p*y,i[4]=o*p*v-a*g*v-o*f*_+n*g*_+a*f*y-n*p*y,i[8]=a*d*v-o*c*v+o*h*_-n*d*_-a*h*y+n*c*y,i[12]=o*c*f-a*d*f-o*h*p+n*d*p+a*h*g-n*c*g,i[1]=d*p*m-c*g*m-d*u*_+l*g*_+c*u*y-l*p*y,i[5]=a*g*m-o*p*m+o*u*_-s*g*_-a*u*y+s*p*y,i[9]=o*c*m-a*d*m-o*l*_+s*d*_+a*l*y-s*c*y,i[13]=a*d*u-o*c*u+o*l*p-s*d*p-a*l*g+s*c*g,i[2]=h*g*m-d*f*m+d*u*v-l*g*v-h*u*y+l*f*y,i[6]=o*f*m-n*g*m-o*u*v+s*g*v+n*u*y-s*f*y,i[10]=n*d*m-o*h*m+o*l*v-s*d*v-n*l*y+s*h*y,i[14]=o*h*u-n*d*u-o*l*f+s*d*f+n*l*g-s*h*g,i[3]=c*f*m-h*p*m-c*u*v+l*p*v+h*u*_-l*f*_,i[7]=n*p*m-a*f*m+a*u*v-s*p*v-n*u*_+s*f*_,i[11]=a*h*m-n*c*m-a*l*v+s*c*v+n*l*_-s*h*_,i[15]=n*c*u-a*h*u+a*l*f-s*c*f-n*l*p+s*h*p;const b=s*i[0]+l*i[4]+u*i[8]+m*i[12];if(0===b){const t="Matrix4.getInverse(): can't invert matrix, determinant is 0";if(e)throw new Error(t);return console.warn(t),this.identity(),this}return this.multiplyScalar(1/b),this}isReflected(){const t=this.elements,e=t[0],i=t[4],r=t[8],s=t[1],n=t[5],a=t[9],o=t[2],l=t[6],h=t[10];return e*n*h+s*l*r+o*i*a-o*n*r-s*i*h-e*l*a<0}scale(t){const e=this.elements,{x:i}=t,{y:r}=t,{z:s}=t;return e[0]*=i,e[4]*=r,e[8]*=s,e[1]*=i,e[5]*=r,e[9]*=s,e[2]*=i,e[6]*=r,e[10]*=s,e[3]*=i,e[7]*=r,e[11]*=s,this}getMaxScaleOnAxis(){const t=this.elements,e=t[0]*t[0]+t[1]*t[1]+t[2]*t[2],i=t[4]*t[4]+t[5]*t[5]+t[6]*t[6],r=t[8]*t[8]+t[9]*t[9]+t[10]*t[10];return Math.sqrt(Math.max(e,Math.max(i,r)))}makeFrustum(t,e,i,r,s,n){const a=this.elements,o=2*s/(e-t),l=2*s/(r-i),h=(e+t)/(e-t),c=(r+i)/(r-i),d=-(n+s)/(n-s),u=-2*n*s/(n-s);return a[0]=o,a[4]=0,a[8]=h,a[12]=0,a[1]=0,a[5]=l,a[9]=c,a[13]=0,a[2]=0,a[6]=0,a[10]=d,a[14]=u,a[3]=0,a[7]=0,a[11]=-1,a[15]=0,this}makePerspective(t,e,i,r){const s=i*Math.tan(n(.5*t)),a=-s,o=a*e,l=s*e;return this.makeFrustum(o,l,a,s,i,r)}makeOrthographic(t,e,i,r,s,n){const a=this.elements,o=1/(e-t),l=1/(i-r),h=1/(n-s),c=(e+t)*o,d=(i+r)*l,u=(n+s)*h;return a[0]=2*o,a[4]=0,a[8]=0,a[12]=-c,a[1]=0,a[5]=2*l,a[9]=0,a[13]=-d,a[2]=0,a[6]=0,a[10]=-2*h,a[14]=-u,a[3]=0,a[7]=0,a[11]=0,a[15]=1,this}isEqual(t){const e=t.elements,i=this.elements;return i[0]===e[0]&&i[4]===e[4]&&i[8]===e[8]&&i[12]===e[12]&&i[1]===e[1]&&i[5]===e[5]&&i[9]===e[9]&&i[13]===e[13]&&i[2]===e[2]&&i[6]===e[6]&&i[10]===e[10]&&i[14]===e[14]&&i[3]===e[3]&&i[7]===e[7]&&i[11]===e[11]&&i[15]===e[15]}clone(){const t=this.elements;return new Matrix4(t[0],t[4],t[8],t[12],t[1],t[5],t[9],t[13],t[2],t[6],t[10],t[14],t[3],t[7],t[11],t[15])}isIdentity(){const t=this.elements;return 1===t[0]&&0===t[4]&&0===t[8]&&0===t[12]&&0===t[1]&&1===t[5]&&0===t[9]&&0===t[13]&&0===t[2]&&0===t[6]&&1===t[10]&&0===t[14]&&0===t[3]&&0===t[7]&&0===t[11]&&1===t[15]}isNearlyIdentity(t){return this.clone().snap(t).isIdentity()}getScale(t){const e=this.elements;return t=t||new Vector3,l.set(e[0],e[1],e[2]),h.set(e[4],e[5],e[6]),c.set(e[8],e[9],e[10]),t.x=l.length(),t.y=h.length(),t.z=c.length(),t}lookAt(t,e,i){const r=this.elements;return c.subVectors(t,e).normalize(),0===c.length()&&(c.z=1),l.crossVectors(i,c).normalize(),0===l.length()&&(c.x+=1e-4,l.crossVectors(i,c).normalize()),h.crossVectors(c,l),r[0]=l.x,r[4]=h.x,r[8]=c.x,r[1]=l.y,r[5]=h.y,r[9]=c.y,r[2]=l.z,r[6]=h.z,r[10]=c.z,this}compose(t,e,i){const r=this.elements;return a.identity(),a.setRotationFromQuaternion(e),o.makeScale(i.x,i.y,i.z),this.multiplyMatrices(a,o),r[12]=t.x,r[13]=t.y,r[14]=t.z,this}}a=new Matrix4,o=new Matrix4;class Vector3{constructor(t,e,i){this.x=t||0,this.y=e||0,this.z=i||0,this.atomid=void 0}set(t,e,i){return this.x=t,this.y=e,this.z=i,this}copy(t){return this.x=t.x,this.y=t.y,this.z=t.z,this}add(t){return this.x+=t.x,this.y+=t.y,this.z+=t.z,this}addVectors(t,e){return this.x=t.x+e.x,this.y=t.y+e.y,this.z=t.z+e.z,this}multiplyVectors(t,e){return this.x=t.x*e.x,this.y=t.y*e.y,this.z=t.z*e.z,this}sub(t){return this.x-=t.x,this.y-=t.y,this.z-=t.z,this}subVectors(t,e){return this.x=t.x-e.x,this.y=t.y-e.y,this.z=t.z-e.z,this}multiplyScalar(t){return this.x*=t,this.y*=t,this.z*=t,this}divideScalar(t){return 0!==t?(this.x/=t,this.y/=t,this.z/=t):(this.x=0,this.y=0,this.z=0),this}max(t){return this.x=Math.max(this.x,t.x),this.y=Math.max(this.y,t.y),this.z=Math.max(this.z,t.z),this}min(t){return this.x=Math.min(this.x,t.x),this.y=Math.min(this.y,t.y),this.z=Math.min(this.z,t.z),this}distanceTo(t){return Math.sqrt(this.distanceToSquared(t))}distanceToSquared(t){const e=this.x-t.x,i=this.y-t.y,r=this.z-t.z;return e*e+i*i+r*r}applyMatrix3(t){const{x:e}=this,{y:i}=this,{z:r}=this,s=t.elements;return this.x=s[0]*e+s[3]*i+s[6]*r,this.y=s[1]*e+s[4]*i+s[7]*r,this.z=s[2]*e+s[5]*i+s[8]*r,this}applyMatrix4(t){const{x:e}=this,{y:i}=this,{z:r}=this,s=t.elements;return this.x=s[0]*e+s[4]*i+s[8]*r+s[12],this.y=s[1]*e+s[5]*i+s[9]*r+s[13],this.z=s[2]*e+s[6]*i+s[10]*r+s[14],this}applyProjection(t){const{x:e}=this,{y:i}=this,{z:r}=this,s=t.elements,n=s[3]*e+s[7]*i+s[11]*r+s[15];return this.x=(s[0]*e+s[4]*i+s[8]*r+s[12])/n,this.y=(s[1]*e+s[5]*i+s[9]*r+s[13])/n,this.z=(s[2]*e+s[6]*i+s[10]*r+s[14])/n,this}applyQuaternion(t){const{x:e}=this,{y:i}=this,{z:r}=this,s=t.x,n=t.y,a=t.z,o=t.w,l={};l.x=2*(i*a-r*n),l.y=2*(r*s-e*a),l.z=2*(e*n-i*s);const h={};return h.x=l.y*a-l.z*n,h.y=l.z*s-l.x*a,h.z=l.x*n-l.y*s,this.x=e+o*l.x+h.x,this.y=i+o*l.y+h.y,this.z=r+o*l.z+h.z,this}negate(){return this.multiplyScalar(-1)}dot(t){return this.x*t.x+this.y*t.y+this.z*t.z}length(){return Math.sqrt(this.x*this.x+this.y*this.y+this.z*this.z)}lengthSq(){return this.x*this.x+this.y*this.y+this.z*this.z}normalize(){return this.divideScalar(this.length())}cross(t){const{x:e}=this,{y:i}=this,{z:r}=this;return this.x=i*t.z-r*t.y,this.y=r*t.x-e*t.z,this.z=e*t.y-i*t.x,this}crossVectors(t,e){return this.x=t.y*e.z-t.z*e.y,this.y=t.z*e.x-t.x*e.z,this.z=t.x*e.y-t.y*e.x,this}equals(t){return this.x==t.x&&this.y==t.y&&this.z==t.z}getPositionFromMatrix(t){return this.x=t.elements[12],this.y=t.elements[13],this.z=t.elements[14],this}setEulerFromRotationMatrix(t,e){const i=t.elements,s=i[0],n=i[4],a=i[8],o=i[5],l=i[9],h=i[6],c=i[10];return void 0===e||"XYZ"===e?(this.y=Math.asin(r(a,-1,1)),Math.abs(a)<.99999?(this.x=Math.atan2(-l,c),this.z=Math.atan2(-n,s)):(this.x=Math.atan2(h,o),this.z=0)):console.error(`Error with vector's setEulerFromRotationMatrix: Unknown order: ${e}`),this}rotateAboutVector(t,e){t.normalize();const i=Math.cos(e),r=Math.sin(e),s=this.clone().multiplyScalar(i),n=t.clone().cross(this).multiplyScalar(r),a=t.clone().multiplyScalar(t.clone().dot(this)).multiplyScalar(1-i),o=s.add(n).add(a);return this.x=o.x,this.y=o.y,this.z=o.z,this}setFromMatrixPosition(t){const e=t.elements;return this.x=e[12],this.y=e[13],this.z=e[14],this}transformDirection(t){const{x:e}=this,{y:i}=this,{z:r}=this,s=t.elements;return this.x=s[0]*e+s[4]*i+s[8]*r,this.y=s[1]*e+s[5]*i+s[9]*r,this.z=s[2]*e+s[6]*i+s[10]*r,this.normalize()}clone(){return new Vector3(this.x,this.y,this.z)}unproject(t){const e=a;return e.multiplyMatrices(t.matrixWorld,e.getInverse(t.projectionMatrix)),this.applyMatrix4(e)}}l=new Vector3,h=new Vector3,c=new Vector3;class Matrix3{constructor(t=1,e=0,i=0,r=0,s=1,n=0,a=0,o=0,l=1){this.elements=new Float32Array(9),this.set(t,e,i,r,s,n,a,o,l)}set(t,e,i,r,s,n,a,o,l){const h=this.elements;return h[0]=t,h[3]=e,h[6]=i,h[1]=r,h[4]=s,h[7]=n,h[2]=a,h[5]=o,h[8]=l,this}identity(){return this.set(1,0,0,0,1,0,0,0,1),this}copy(t){const e=t.elements;this.set(e[0],e[3],e[6],e[1],e[4],e[7],e[2],e[5],e[8])}multiplyScalar(t){const e=this.elements;return e[0]*=t,e[3]*=t,e[6]*=t,e[1]*=t,e[4]*=t,e[7]*=t,e[2]*=t,e[5]*=t,e[8]*=t,this}getInverse3(t){const e=t.elements,i=this.elements;i[0]=e[4]*e[8]-e[5]*e[7],i[3]=e[6]*e[5]-e[3]*e[8],i[6]=e[3]*e[7]-e[6]*e[4],i[1]=e[7]*e[2]-e[1]*e[8],i[4]=e[0]*e[8]-e[6]*e[2],i[7]=e[1]*e[6]-e[0]*e[7],i[2]=e[1]*e[5]-e[2]*e[4],i[5]=e[2]*e[3]-e[0]*e[5],i[8]=e[0]*e[4]-e[1]*e[3];const r=e[0]*i[0]+e[3]*i[1]+e[6]*i[2];return this.multiplyScalar(1/r),this}getInverse(t,e){const i=t.elements,r=this.elements;r[0]=i[10]*i[5]-i[6]*i[9],r[1]=-i[10]*i[1]+i[2]*i[9],r[2]=i[6]*i[1]-i[2]*i[5],r[3]=-i[10]*i[4]+i[6]*i[8],r[4]=i[10]*i[0]-i[2]*i[8],r[5]=-i[6]*i[0]+i[2]*i[4],r[6]=i[9]*i[4]-i[5]*i[8],r[7]=-i[9]*i[0]+i[1]*i[8],r[8]=i[5]*i[0]-i[1]*i[4];const s=i[0]*r[0]+i[1]*r[3]+i[2]*r[6];if(0===s){const t="Matrix3.getInverse(): can't invert matrix, determinant is 0";if(e)throw new Error(t);return console.warn(t),this.identity(),this}return this.multiplyScalar(1/s),this}getDeterminant(){const t=this.elements;return t[0]*t[4]*t[8]+t[1]*t[5]*t[6]+t[2]*t[3]*t[7]-t[2]*t[4]*t[6]-t[1]*t[3]*t[8]-t[0]*t[5]*t[7]}transpose(){let t;const e=this.elements;return t=e[1],e[1]=e[3],e[3]=t,t=e[2],e[2]=e[6],e[6]=t,t=e[5],e[5]=e[7],e[7]=t,this}clone(){const t=this.elements;return new Matrix3(t[0],t[3],t[6],t[1],t[4],t[7],t[2],t[5],t[8])}getMatrix4(){const t=this.elements;return new Matrix4(t[0],t[3],t[6],0,t[1],t[4],t[7],0,t[2],t[5],t[8],0)}}class Ray{constructor(t,e){this.origin=void 0!==t?t:new Vector3,this.direction=void 0!==e?e:new Vector3}set(t,e){return this.origin.copy(t),this.direction.copy(e),this}copy(t){return this.origin.copy(t.origin),this.direction.copy(t.direction),this}at(t,e){return(e||new Vector3).copy(this.direction).multiplyScalar(t).add(this.origin)}recast(t){const e=l;return this.origin.copy(this.at(t,e)),this}closestPointToPoint(t,e){const i=e||new Vector3;i.subVectors(t,this.origin);const r=i.dot(this.direction);return i.copy(this.direction).multiplyScalar(r).add(this.origin)}distanceToPoint(t){const e=l,i=e.subVectors(t,this.origin).dot(this.direction);return e.copy(this.direction).multiplyScalar(i).add(this.origin),e.distanceTo(t)}isIntersectionCylinder(){}isIntersectionSphere(t){return this.distanceToPoint(t.center)<=t.radius}isIntersectionPlane(t){return 0!==t.normal.dot(this.direction)||0===t.distanceToPoint(this.origin)}distanceToPlane(t){const e=t.normal.dot(this.direction);if(0===e)return 0===t.distanceToPoint(this.origin)?0:void 0;return-(this.origin.dot(t.normal)+t.constant)/e}intersectPlane(t,e){const i=this.distanceToPlane(t);if(void 0!==i)return this.at(i,e)}applyMatrix4(t){return this.direction.add(this.origin).applyMatrix4(t),this.origin.applyMatrix4(t),this.direction.sub(this.origin),this}clone(){return(new Ray).copy(this)}}function d(t,e,i,r,s,n){r=r*Math.PI/180,s=s*Math.PI/180,n=n*Math.PI/180;const a=t=>t*t,o=Math.cos(r),l=Math.cos(s),h=Math.cos(n),c=Math.sin(n);return new Matrix3(t,e*h,i*l,0,e*c,i*(o-l*h)/c,0,0,i*Math.sqrt(1-a(o)-a(l)-a(h)+2*o*l*h)/c)}},99:(t,e,i)=>{"use strict";i.r(e),i.d(e,{Cylinder:()=>Cylinder,Sphere:()=>Sphere,Triangle:()=>Triangle});var r=i(529);class Sphere{constructor(t={x:0,y:0,z:0},e=0){this.center=new r.Vector3(t.x,t.y,t.z),this.radius=e}set(t,e){return this.center.copy(t),this.radius=e,this}copy(t){return this.center.copy(t.center),this.radius=t.radius,this}applyMatrix4(t){return this.center.applyMatrix4(t),this.radius=this.radius*t.getMaxScaleOnAxis(),this}translate(t){return this.center.add(t),this}equals(t){return t.center.equals(this.center)&&t.radius===this.radius}clone(){return(new Sphere).copy(this)}}let s=new r.Vector3;class Cylinder{constructor(t=new r.Vector3,e=new r.Vector3,i=0){this.c1=t,this.c2=e,this.radius=i,this.direction=(new r.Vector3).subVectors(this.c2,this.c1).normalize()}copy(t){return this.c1.copy(t.c1),this.c2.copy(t.c2),this.direction.copy(t.direction),this.radius=t.radius,this}lengthSq(){return s.subVectors(this.c2,this.c1).lengthSq()}applyMatrix4(t){return this.direction.add(this.c1).applyMatrix4(t),this.c1.applyMatrix4(t),this.c2.applyMatrix4(t),this.direction.sub(this.c1).normalize(),this.radius=this.radius*t.getMaxScaleOnAxis(),this}}const n=new r.Vector3;class Triangle{constructor(t=new r.Vector3,e=new r.Vector3,i=new r.Vector3){this.a=t,this.b=e,this.c=i}copy(t){return this.a.copy(t.a),this.b.copy(t.b),this.c.copy(t.c),this}applyMatrix4(t){return this.a.applyMatrix4(t),this.b.applyMatrix4(t),this.c.applyMatrix4(t),this}getNormal(){var t=this.a.clone();return t.sub(this.b),n.subVectors(this.c,this.b),t.cross(n),t.normalize(),t}}},222:(t,e,i)=>{"use strict";i.r(e),i.d(e,{CC:()=>CC,Color:()=>Color,builtinColorSchemes:()=>h,chains:()=>l,elementColors:()=>a,htmlColors:()=>r,residues:()=>o,ssColors:()=>s});class Color{constructor(t,e,i){return this.r=0,this.g=0,this.b=0,arguments.length>1&&"number"==typeof t?(this.r=t||0,this.g=e||0,this.b=i||0,this):this.set(t||0)}set(t){return t instanceof Color?t.clone():("number"==typeof t?this.setHex(t):"object"==typeof t&&(this.r=(null==t?void 0:t.r)||0,this.g=(null==t?void 0:t.g)||0,this.b=(null==t?void 0:t.b)||0),this)}setHex(t){return t=Math.floor(t),this.r=(t>>16&255)/255,this.g=(t>>8&255)/255,this.b=(255&t)/255,this}getHex(){return Math.round(255*this.r)<<16|Math.round(255*this.g)<<8|Math.round(255*this.b)}clone(){return new Color(this.r,this.g,this.b)}copy(t){return this.r=t.r,this.g=t.g,this.b=t.b,this}scaled(){var t={};return t.r=Math.round(255*this.r),t.g=Math.round(255*this.g),t.b=Math.round(255*this.b),t.a=1,t}}class CC{static color(t){if(!t)return CC.cache[0];if(t instanceof Color)return t;if("number"==typeof t&&void 0!==CC.cache[t])return CC.cache[t];if(t&&Array.isArray(t))return t.map(CC.color);let e=CC.getHex(t),i=new Color(e);return CC.cache[e]=i,i}static getHex(t){var e;if(Array.isArray(t))return t.map(CC.getHex);if("string"==typeof t){let i=t;if(!isNaN(parseInt(i)))return parseInt(i);if(i=i.trim(),4==i.length&&"#"==i[0]&&(i="#"+i[1]+i[1]+i[2]+i[2]+i[3]+i[3]),7==i.length&&"#"==i[0])return parseInt(i.substring(1),16);let r=CC.rgbRegEx.exec(i);if(r){""!=r[1]&&console.log("WARNING: Opacity value in rgba ignored. Specify separately as opacity attribute.");let t=0;for(let e=2;e<5;e++){t*=256;let i=r[e].endsWith("%")?255*parseFloat(r[e])/100:parseFloat(r[e]);t+=Math.round(i)}return t}return(null===(e=null===window||void 0===window?void 0:window.$3Dmol)||void 0===e?void 0:e.htmlColors[t.toLowerCase()])||0}return t}}CC.rgbRegEx=/rgb(a?)\(\s*([^ ,\)\t]+)\s*,\s*([^ ,\)\t]+)\s*,\s*([^ ,\)\t]+)/i,CC.cache={0:new Color(0)};const r={aliceblue:15792383,antiquewhite:16444375,aqua:65535,aquamarine:8388564,azure:15794175,beige:16119260,bisque:16770244,black:0,blanchedalmond:16772045,blue:255,blueviolet:9055202,brown:10824234,burlywood:14596231,cadetblue:6266528,chartreuse:8388352,chocolate:13789470,coral:16744272,cornflowerblue:6591981,cornsilk:16775388,crimson:14423100,cyan:65535,darkblue:139,darkcyan:35723,darkgoldenrod:12092939,darkgray:11119017,darkgrey:11119017,darkgreen:25600,darkkhaki:12433259,darkmagenta:9109643,darkolivegreen:5597999,darkorange:16747520,darkorchid:10040012,darkred:9109504,darksalmon:15308410,darkseagreen:9419919,darkslateblue:4734347,darkslategray:3100495,darkslategrey:3100495,darkturquoise:52945,darkviolet:9699539,deeppink:16716947,deepskyblue:49151,dimgray:6908265,dimgrey:6908265,dodgerblue:2003199,firebrick:11674146,floralwhite:16775920,forestgreen:2263842,fuchsia:16711935,gainsboro:14474460,ghostwhite:16316671,gold:16766720,goldenrod:14329120,gray:8421504,grey:8421504,green:32768,greenyellow:11403055,honeydew:15794160,hotpink:16738740,indianred:13458524,indigo:4915330,ivory:16777200,khaki:15787660,lavender:15132410,lavenderblush:16773365,lawngreen:8190976,lemonchiffon:16775885,lightblue:11393254,lightcoral:15761536,lightcyan:14745599,lightgoldenrodyellow:16448210,lightgray:13882323,lightgrey:13882323,lightgreen:9498256,lightpink:16758465,lightsalmon:16752762,lightseagreen:2142890,lightskyblue:8900346,lightslategray:7833753,lightslategrey:7833753,lightsteelblue:11584734,lightyellow:16777184,lime:65280,limegreen:3329330,linen:16445670,magenta:16711935,maroon:8388608,mediumaquamarine:6737322,mediumblue:205,mediumorchid:12211667,mediumpurple:9662683,mediumseagreen:3978097,mediumslateblue:8087790,mediumspringgreen:64154,mediumturquoise:4772300,mediumvioletred:13047173,midnightblue:1644912,mintcream:16121850,mistyrose:16770273,moccasin:16770229,navajowhite:16768685,navy:128,oldlace:16643558,olive:8421376,olivedrab:7048739,orange:16753920,orangered:16729344,orchid:14315734,palegoldenrod:15657130,palegreen:10025880,paleturquoise:11529966,palevioletred:14381203,papayawhip:16773077,peachpuff:16767673,peru:13468991,pink:16761035,plum:14524637,powderblue:11591910,purple:8388736,rebeccapurple:6697881,red:16711680,rosybrown:12357519,royalblue:4286945,saddlebrown:9127187,salmon:16416882,sandybrown:16032864,seagreen:3050327,seashell:16774638,sienna:10506797,silver:12632256,skyblue:8900331,slateblue:6970061,slategray:7372944,slategrey:7372944,snow:16775930,springgreen:65407,steelblue:4620980,tan:13808780,teal:32896,thistle:14204888,tomato:16737095,turquoise:4251856,violet:15631086,wheat:16113331,white:16777215,whitesmoke:16119285,yellow:16776960,yellowgreen:10145074},s={pyMol:{h:16711680,s:16776960,c:65280},Jmol:{h:16711808,s:16762880,c:16777215}},n={H:16777215,He:16761035,HE:16761035,Li:11674146,LI:11674146,B:65280,C:13158600,N:9408511,O:15728640,F:14329120,Na:255,NA:255,Mg:2263842,MG:2263842,Al:8421520,AL:8421520,Si:14329120,SI:14329120,P:16753920,S:16762930,Cl:65280,CL:65280,Ca:8421520,CA:8421520,Ti:8421520,TI:8421520,Cr:8421520,CR:8421520,Mn:8421520,MN:8421520,Fe:16753920,FE:16753920,Ni:10824234,NI:10824234,Cu:10824234,CU:10824234,Zn:10824234,ZN:10824234,Br:10824234,BR:10824234,Ag:8421520,AG:8421520,I:10494192,Ba:16753920,BA:16753920,Au:14329120,AU:14329120},a={defaultColor:16716947,Jmol:{H:16777215,He:14286847,HE:14286847,Li:13402367,LI:13402367,Be:12779264,BE:12779264,B:16758197,C:9474192,N:3166456,O:16715021,F:9494608,Ne:11789301,NE:11789301,Na:11230450,NA:11230450,Mg:9109248,MG:9109248,Al:12560038,AL:12560038,Si:1578e4,SI:1578e4,P:16744448,S:16777008,Cl:2093087,CL:2093087,Ar:8442339,AR:8442339,K:9388244,Ca:4062976,CA:4062976,Sc:15132390,SC:15132390,Ti:12567239,TI:12567239,V:10921643,Cr:9083335,CR:9083335,Mn:10255047,MN:10255047,Fe:14706227,FE:14706227,Co:15765664,CO:15765664,Ni:5296208,NI:5296208,Cu:13140019,CU:13140019,Zn:8224944,ZN:8224944,Ga:12750735,GA:12750735,Ge:6721423,GE:6721423,As:12419299,AS:12419299,Se:16752896,SE:16752896,Br:10889513,BR:10889513,Kr:6076625,KR:6076625,Rb:7351984,RB:7351984,Sr:65280,SR:65280,Y:9764863,Zr:9756896,ZR:9756896,Nb:7586505,NB:7586505,Mo:5551541,MO:5551541,Tc:3907230,TC:3907230,Ru:2396047,RU:2396047,Rh:687500,RH:687500,Pd:27013,PD:27013,Ag:12632256,AG:12632256,Cd:16767375,CD:16767375,In:10909043,IN:10909043,Sn:6717568,SN:6717568,Sb:10380213,SB:10380213,Te:13924864,TE:13924864,I:9699476,Xe:4366e3,XE:4366e3,Cs:5707663,CS:5707663,Ba:51456,BA:51456,La:7394559,LA:7394559,Ce:16777159,CE:16777159,Pr:14286791,PR:14286791,Nd:13107143,ND:13107143,Pm:10747847,PM:10747847,Sm:9437127,SM:9437127,Eu:6422471,EU:6422471,Gd:4587463,GD:4587463,Tb:3211207,TB:3211207,Dy:2097095,DY:2097095,Ho:65436,HO:65436,Er:58997,ER:58997,Tm:54354,TM:54354,Yb:48952,YB:48952,Lu:43812,LU:43812,Hf:5096191,HF:5096191,Ta:5089023,TA:5089023,W:2200790,Re:2522539,RE:2522539,Os:2516630,OS:2516630,Ir:1528967,IR:1528967,Pt:13684960,PT:13684960,Au:16765219,AU:16765219,Hg:12105936,HG:12105936,Tl:10900557,TL:10900557,Pb:5724513,PB:5724513,Bi:10375093,BI:10375093,Po:11230208,PO:11230208,At:7688005,AT:7688005,Rn:4358806,RN:4358806,Fr:4325478,FR:4325478,Ra:32e3,RA:32e3,Ac:7384058,AC:7384058,Th:47871,TH:47871,Pa:41471,PA:41471,U:36863,Np:33023,NP:33023,Pu:27647,PU:27647,Am:5528818,AM:5528818,Cm:7888099,CM:7888099,Bk:9064419,BK:9064419,Cf:10565332,CF:10565332,Es:11739092,ES:11739092,Fm:11739066,FM:11739066,Md:11734438,MD:11734438,No:12389767,NO:12389767,Lr:13041766,LR:13041766,Rf:13369433,RF:13369433,Db:13697103,DB:13697103,Sg:14221381,SG:14221381,Bh:14680120,BH:14680120,Hs:15073326,HS:15073326,Mt:15400998,MT:15400998},rasmol:n,defaultColors:Object.assign({},n),greenCarbon:Object.assign(Object.assign({},n),{C:65280}),cyanCarbon:Object.assign(Object.assign({},n),{C:65535}),magentaCarbon:Object.assign(Object.assign({},n),{C:16711935}),yellowCarbon:Object.assign(Object.assign({},n),{C:16776960}),whiteCarbon:Object.assign(Object.assign({},n),{C:16777215}),orangeCarbon:Object.assign(Object.assign({},n),{C:16753920}),purpleCarbon:Object.assign(Object.assign({},n),{C:8388736}),blueCarbon:Object.assign(Object.assign({},n),{C:255})},o={amino:{ALA:13158600,ARG:1334015,ASN:56540,ASP:15075850,CYS:15132160,GLN:56540,GLU:15075850,GLY:15461355,HIS:8553170,ILE:1016335,LEU:1016335,LYS:1334015,MET:15132160,PHE:3289770,PRO:14456450,SER:16422400,THR:16422400,TRP:11819700,TYR:3289770,VAL:1016335,ASX:16738740,GLX:16738740},shapely:{ALA:9240460,ARG:124,ASN:16743536,ASP:10485826,CYS:16777072,GLN:16731212,GLU:6684672,GLY:16777215,HIS:7368959,ILE:19456,LEU:4546117,LYS:4671416,MET:12099650,PHE:5459026,PRO:5395026,SER:16740418,THR:12078080,TRP:5195264,TYR:9203788,VAL:16747775,ASX:16711935,GLX:16711935},nucleic:{A:10526975,G:16740464,I:8454143,C:16747595,T:10551200,U:16744576}},l={atom:{A:12636415,B:11599792,C:16761032,D:16777088,E:16761087,F:11596016,G:16765040,H:15761536,I:16113331,J:49151,K:13458524,L:6737322,M:10145074,N:15631086,O:52945,P:65407,Q:3978097,R:139,S:12433259,T:25600,U:8388608,V:8421376,W:8388736,X:32896,Y:12092939,Z:11674146},hetatm:{A:9478351,B:8441752,C:13602992,D:13619056,E:13603023,F:8437952,G:13607008,H:12603504,I:12955267,J:42959,K:11881548,L:5682578,M:9090346,N:12481214,O:46753,P:53103,Q:3447649,R:187,S:10854235,T:37888,U:11534336,V:11579392,W:11534512,X:45232,Y:15250963,Z:12726834}},h={ssPyMol:{prop:"ss",map:s.pyMol},ssJmol:{prop:"ss",map:s.Jmol},Jmol:{prop:"elem",map:a.Jmol},amino:{prop:"resn",map:o.amino},shapely:{prop:"resn",map:o.shapely},nucleic:{prop:"resn",map:o.nucleic},chain:{prop:"chain",map:l.atom},rasmol:{prop:"elem",map:a.rasmol},default:{prop:"elem",map:a.defaultColors},greenCarbon:{prop:"elem",map:a.greenCarbon},chainHetatm:{prop:"chain",map:l.hetatm},cyanCarbon:{prop:"elem",map:a.cyanCarbon},magentaCarbon:{prop:"elem",map:a.magentaCarbon},purpleCarbon:{prop:"elem",map:a.purpleCarbon},whiteCarbon:{prop:"elem",map:a.whiteCarbon},orangeCarbon:{prop:"elem",map:a.orangeCarbon},yellowCarbon:{prop:"elem",map:a.yellowCarbon},blueCarbon:{prop:"elem",map:a.blueCarbon}}},421:(t,e,i)=>{"use strict";i.r(e),i.d(e,{CAP:()=>it,CC:()=>s.CC,CONTEXTS_PER_VIEWPORT:()=>Wt,Color:()=>s.Color,CustomLinear:()=>r.CustomLinear,Cylinder:()=>J.Cylinder,GLDraw:()=>rt,GLModel:()=>GLModel,GLShape:()=>GLShape,GLViewer:()=>GLViewer,GLVolumetricRender:()=>GLVolumetricRender,Gradient:()=>r.Gradient,GradientType:()=>r.GradientType,Label:()=>Label,LabelCount:()=>a,MarchingCube:()=>MarchingCube,MarchingCubeInitializer:()=>MarchingCubeInitializer,Matrix3:()=>$.Matrix3,Matrix4:()=>$.Matrix4,Parsers:()=>K,PausableTimer:()=>L.PausableTimer,PointGrid:()=>PointGrid,ProteinSurface:()=>ProteinSurface,Quaternion:()=>$.Quaternion,ROYGB:()=>r.ROYGB,RWB:()=>r.RWB,Ray:()=>$.Ray,Sinebow:()=>r.Sinebow,Sphere:()=>J.Sphere,SurfaceType:()=>Q,Triangle:()=>J.Triangle,Vector2:()=>$.Vector2,Vector3:()=>$.Vector3,VolumeData:()=>st.VolumeData,adjustVolumeStyle:()=>L.adjustVolumeStyle,applyPartialCharges:()=>h,autoinit:()=>Xt,autoload:()=>$t,base64ToArray:()=>L.base64ToArray,bondLength:()=>C.bondLength,builtinColorSchemes:()=>s.builtinColorSchemes,builtinGradients:()=>r.builtinGradients,chains:()=>s.chains,clamp:()=>$.clamp,conversionMatrix3:()=>$.conversionMatrix3,createStereoViewer:()=>Zt,createViewer:()=>qt,createViewerGrid:()=>Yt,deepCopy:()=>L.deepCopy,degToRad:()=>$.degToRad,download:()=>L.download,drawCartoon:()=>xt,elementColors:()=>s.elementColors,extend:()=>L.extend,get:()=>L.get,getAtomProperty:()=>L.getAtomProperty,getColorFromStyle:()=>L.getColorFromStyle,getElement:()=>L.getElement,getExtent:()=>L.getExtent,getGradient:()=>r.getGradient,getPropertyRange:()=>L.getPropertyRange,getbin:()=>L.getbin,htmlColors:()=>s.htmlColors,inflateString:()=>L.inflateString,isEmptyObject:()=>L.isEmptyObject,isNumeric:()=>L.isNumeric,makeFunction:()=>L.makeFunction,mergeGeos:()=>L.mergeGeos,normalizeValue:()=>r.normalizeValue,partialCharges:()=>l,processing_autoinit:()=>Kt,residues:()=>s.residues,setBondLength:()=>C.setBondLength,setSyncSurface:()=>et,specStringToObject:()=>L.specStringToObject,splitMesh:()=>wt,ssColors:()=>s.ssColors,subdivide_spline:()=>nt,syncSurface:()=>tt,viewers:()=>Qt});var r=i(546),s=i(222),n=i(638);let a=0;function o(t,e,i){var r=i;return void 0!==t&&(t instanceof s.Color?r=t.scaled():void 0!==(r=s.CC.color(t)).scaled&&(r=r.scaled())),void 0!==e&&(r.a=parseFloat(e)),r}class Label{constructor(t,e){this.id=a++,this.stylespec=e||{},this.canvas=document.createElement("canvas"),this.canvas.width=134,this.canvas.height=35,this.context=this.canvas.getContext("2d"),this.sprite=new n.Sprite,this.text=t,this.frame=this.stylespec.frame}getStyle(){return this.stylespec}hide(){this.sprite&&(this.sprite.visible=!1)}show(){this.sprite&&(this.sprite.visible=!0)}setContext(){var t=this.stylespec,e=void 0!==t.useScreen&&t.useScreen,i=t.showBackground;"0"!==i&&"false"!==i||(i=!1),void 0===i&&(i=!0);var s=t.font?t.font:"sans-serif",a=parseInt(t.fontSize)?parseInt(t.fontSize):18,l=o(t.fontColor,t.fontOpacity,{r:255,g:255,b:255,a:1}),h=t.padding?t.padding:4,c=t.borderThickness?t.borderThickness:0,d=o(t.backgroundColor,t.backgroundOpacity,{r:0,g:0,b:0,a:1}),u=o(t.borderColor,t.borderOpacity,d),f=t.position?t.position:{x:-10,y:1,z:1},p=void 0===t.inFront||t.inFront;"false"!==p&&"0"!==p||(p=!1);var g=t.alignment||n.SpriteAlignment.topLeft;"string"==typeof g&&g in n.SpriteAlignment&&(g=n.SpriteAlignment[g]);var m="";t.bold&&(m="bold "),this.context.font=m+a+"px "+s;var v=this.context.measureText(this.text).width;i||(c=0);var _=v+2.5*c+2*h,y=1.25*a+2*c+2*h;if(t.backgroundImage){var b=t.backgroundImage,x=t.backgroundWidth?t.backgroundWidth:b.width,w=t.backgroundHeight?t.backgroundHeight:b.height;x>_&&(_=x),w>y&&(y=w)}if(this.canvas.width=_,this.canvas.height=y,this.context.clearRect(0,0,this.canvas.width,this.canvas.height),m="",t.bold&&(m="bold "),this.context.font=m+a+"px "+s,this.context.fillStyle="rgba("+d.r+","+d.g+","+d.b+","+d.a+")",this.context.strokeStyle="rgba("+u.r+","+u.g+","+u.b+","+u.a+")",t.backgroundGradient){let e=this.context.createLinearGradient(0,y/2,_,y/2),i=r.Gradient.getGradient(t.backgroundGradient),s=i.range(),n=-1,a=1;s&&(n=s[0],a=s[1]);let l=a-n;for(let t=0;t<1.01;t+=.1){let r=o(i.valueToHex(n+l*t)),s="rgba("+r.r+","+r.g+","+r.b+","+r.a+")";e.addColorStop(t,s)}this.context.fillStyle=e}this.context.lineWidth=c,i&&function(t,e,i,r,s,n,a){t.beginPath(),t.moveTo(e+n,i),t.lineTo(e+r-n,i),t.quadraticCurveTo(e+r,i,e+r,i+n),t.lineTo(e+r,i+s-n),t.quadraticCurveTo(e+r,i+s,e+r-n,i+s),t.lineTo(e+n,i+s),t.quadraticCurveTo(e,i+s,e,i+s-n),t.lineTo(e,i+n),t.quadraticCurveTo(e,i,e+n,i),t.closePath(),t.fill(),a&&t.stroke()}(this.context,c,c,_-2*c,y-2*c,6,c>0),t.backgroundImage&&this.context.drawImage(b,0,0,_,y),this.context.fillStyle="rgba("+l.r+","+l.g+","+l.b+","+l.a+")",this.context.fillText(this.text,c+h,a+c+h,v);var A=new n.Texture(this.canvas);A.needsUpdate=!0,this.sprite.material=new n.SpriteMaterial({map:A,useScreenCoordinates:e,alignment:g,depthTest:!p,screenOffset:t.screenOffset||null}),this.sprite.scale.set(1,1,1),this.sprite.position.set(f.x,f.y,f.z)}dispose(){void 0!==this.sprite.material.map&&this.sprite.material.map.dispose(),void 0!==this.sprite.material&&this.sprite.material.dispose()}}const l={"ALA:N":-.15,"ALA:CA":.1,"ALA:CB":0,"ALA:C":.6,"ALA:O":-.55,"ARG:N":-.15,"ARG:CA":.1,"ARG:CB":0,"ARG:CG":0,"ARG:CD":.1,"ARG:NE":-.1,"ARG:CZ":.5,"ARG:NH1":.25,"ARG:NH2":.25,"ARG:C":.6,"ARG:O":-.55,"ASN:N":-.15,"ASN:CA":.1,"ASN:CB":0,"ASN:CG":.55,"ASN:OD1":-.55,"ASN:ND2":0,"ASN:C":.6,"ASN:O":-.55,"ASP:N":-.15,"ASP:CA":.1,"ASP:CB":0,"ASP:CG":.14,"ASP:OD1":-.57,"ASP:OD2":-.57,"ASP:C":.6,"ASP:O":-.55,"CYS:N":-.15,"CYS:CA":.1,"CYS:CB":.19,"CYS:SG":-.19,"CYS:C":.6,"CYS:O":-.55,"GLN:N":-.15,"GLN:CA":.1,"GLN:CB":0,"GLN:CG":0,"GLN:CD":.55,"GLN:OE1":-.55,"GLN:NE2":0,"GLN:C":.6,"GLN:O":-.55,"GLU:N":-.15,"GLU:CA":.1,"GLU:CB":0,"GLU:CG":0,"GLU:CD":.14,"GLU:OE1":-.57,"GLU:OE2":-.57,"GLU:C":.6,"GLU:O":-.55,"GLY:N":-.15,"GLY:CA":.1,"GLY:C":.6,"GLY:O":-.55,"HIS:N":-.15,"HIS:CA":.1,"HIS:CB":0,"HIS:CG":.1,"HIS:ND1":-.1,"HIS:CD2":.1,"HIS:NE2":-.4,"HIS:CE1":.3,"HIS:C":.6,"HIS:O":-.55,"ILE:N":-.15,"ILE:CA":.1,"ILE:CB":0,"ILE:CG2":0,"ILE:CG1":0,"ILE:CD":0,"ILE:C":.6,"ILE:O":-.55,"LEU:N":-.15,"LEU:CA":.1,"LEU:CB":0,"LEU:CG":0,"LEU:CD1":0,"LEU:CD2":0,"LEU:C":.6,"LEU:O":-.55,"LYS:N":-.15,"LYS:CA":.1,"LYS:CB":0,"LYS:CG":0,"LYS:CD":0,"LYS:CE":.25,"LYS:NZ":.75,"LYS:C":.6,"LYS:O":-.55,"MET:N":-.15,"MET:CA":.1,"MET:CB":0,"MET:CG":.06,"MET:SD":-.12,"MET:CE":.06,"MET:C":.6,"MET:O":-.55,"PHE:N":-.15,"PHE:CA":.1,"PHE:CB":0,"PHE:CG":0,"PHE:CD1":0,"PHE:CD2":0,"PHE:CE1":0,"PHE:CE2":0,"PHE:CZ":0,"PHE:C":.6,"PHE:O":-.55,"PRO:N":-.25,"PRO:CD":.1,"PRO:CA":.1,"PRO:CB":0,"PRO:CG":0,"PRO:C":.6,"PRO:O":-.55,"SER:N":-.15,"SER:CA":.1,"SER:CB":.25,"SER:OG":-.25,"SER:C":.6,"SER:O":-.55,"THR:N":-.15,"THR:CA":.1,"THR:CB":.25,"THR:OG1":-.25,"THR:CG2":0,"THR:C":.6,"THR:O":-.55,"TRP:N":-.15,"TRP:CA":.1,"TRP:CB":0,"TRP:CG":-.03,"TRP:CD2":.1,"TRP:CE2":-.04,"TRP:CE3":-.03,"TRP:CD1":.06,"TRP:NE1":-.06,"TRP:CZ2":0,"TRP:CZ3":0,"TRP:CH2":0,"TRP:C":.6,"TRP:O":-.55,"TYR:N":-.15,"TYR:CA":.1,"TYR:CB":0,"TYR:CG":0,"TYR:CD1":0,"TYR:CE1":0,"TYR:CD2":0,"TYR:CE2":0,"TYR:CZ":.25,"TYR:OH":-.25,"TYR:C":.6,"TYR:O":-.55,"VAL:N":-.15,"VAL:CA":.1,"VAL:CB":0,"VAL:CG1":0,"VAL:CG2":0,"VAL:C":.6,"VAL:O":-.55};function h(t,e){if((!e||void 0===t.partialCharge)&&t.resn&&t.atom){var i=t.resn+":"+t.atom;t.properties.partialCharge=l[i]}}var c=i(797),d=i(865),u=i(392);function f(t,e){for(var i=[[]],r=void 0===(e=e||{}).assignBonds||e.assignBonds,s=t.trimStart().split(/\r?\n|\r/);s.length>0&&!(s.length<3);){var a=parseInt(s[0]);if(isNaN(a)||a<=0)break;if(s.length1){var l=new Float32Array(o[1].split(/\s+/)),h=new n.Matrix3(l[0],l[3],l[6],l[1],l[4],l[7],l[2],l[5],l[8]);i.modelData=[{cryst:{matrix:h}}]}for(var c=2,d=i[i.length-1].length,f=d+a,p=d;p=7&&(m.dx=parseFloat(g[4]),m.dy=parseFloat(g[5]),m.dz=parseFloat(g[6]))}if(!e.multimodel)break;i.push([]),s.splice(0,c)}if(r)for(let t=0;t3&&r[3].length>38&&(i=r[3].substring(34,39)),"V2000"===i?function(t,e){var i=[[]],r=!1;for(void 0!==e.keepH&&(r=!e.keepH);t.length>0&&!(t.length<4);){var s=parseInt(t[3].substring(0,3));if(isNaN(s)||s<=0)break;var n=parseInt(t[3].substring(3,6)),a=4;if(t.length<4+s+n)break;var o,l,h=[],c=i[i.length-1].length,d=c+s;for(o=c;o0&&!(t.length<8)&&t[4].startsWith("M V30 BEGIN CTAB")&&t[5].startsWith("M V30 COUNTS")&&!(t[5].length<14);){var s=t[5].substring(13).match(/\S+/g);if(s.length<2)break;var n=parseInt(s[0]);if(isNaN(n)||n<=0)break;var a=parseInt(s[1]),o=7;if(t.length<8+n+a)break;var l,h=[],c=i[i.length-1].length,d=c+n;for(l=c;l4){var f={},p=u[1].replace(/ /g,"");f.atom=f.elem=p[0].toUpperCase()+p.substring(1).toLowerCase(),"H"===f.elem&&r||(f.serial=l,h[l]=i[i.length-1].length,f.x=parseFloat(u[2]),f.y=parseFloat(u[3]),f.z=parseFloat(u[4]),f.hetflag=!0,f.bonds=[],f.bondOrder=[],f.properties={},f.index=i[i.length-1].length,i[i.length-1].push(f))}}if("M V30 END ATOM"!==t[o])break;if(o++,0===a||"M V30 BEGIN BOND"!==t[o])break;for(o++,l=0;l3){var m=h[parseInt(g[2])-1+c],v=h[parseInt(g[3])-1+c],_=parseFloat(g[1]);void 0!==m&&void 0!==v&&(i[i.length-1][m].bonds.push(v),i[i.length-1][m].bondOrder.push(_),i[i.length-1][v].bonds.push(m),i[i.length-1][v].bondOrder.push(_))}}if(!e.multimodel)break;for(e.onemol||i.push([]);"$$$$"!==t[o]&&oi)break;if(t.atom==n.atom)continue;const o=Math.abs(t.y-n.y);if(o>i)continue;const l=Math.abs(t.x-n.x);if(l>i)continue;const h=l*l+o*o+e*e;h>r||t.chain==n.chain&&Math.abs(t.resi-n.resi)<4||(h1.001;)i[t]-=1,r[t]-=1}const s=new n.Vector3(r[0],r[1],r[2]);return s.applyMatrix3(h),s};if(i.normalizeAssembly&&r)for(let i=0;i.001&&(l=i),t[i].translate(s)}if(s){if(t.length>1)for(let i=0;i=0){const i=new n.Vector3;for(let r=0;r1)break;(0,_.areConnected)(r,t,e)&&(-1===r.bonds.indexOf(t.index)&&(r.bonds.push(t.index),r.bondOrder.push(1),t.bonds.push(r.index),t.bondOrder.push(1)),r.resi!==t.resi&&(s=!0))}}}function x(t,e={}){const i=[],r=!e.doAssembly,s=i.modelData=[],a=void 0===e.assignBonds||e.assignBonds;function o(t,e){const i=[];let r=0,s=0;for(;s-1){let e=d.split("");e[t]="_",d=(d=e.join("")).substring(0,t)+"_"+d.substring(t+1)}}h.push(d)}}let u=0;for(;uMOLECULE/),n=t.search(/@ATOM/);if(-1==s||-1==n)return i;for(var a=t.substring(s).split(/\r?\n|\r/);a.length>0;){var o=[],l=a[2].replace(/^\s+/,"").replace(/\s+/g," ").split(" "),h=parseInt(l[0]),c=0;l.length>1&&(c=parseInt(l[1]));var d,u=4;for(d=3;dATOM"==a[d]){u=d+1;break}var f=i[i.length-1].length,p=f+h;for(d=f;dBOND"==a[u++]){b=!0;break}if(b&&c)for(d=0;d0&&"H"===i[0]&&"Hg"!==i&&"He"!==i&&"Hf"!==i&&"Hs"!==i&&"Ho"!==i&&(i="H"),i.length>1&&(i=i[0].toUpperCase()+i.substring(1).toLowerCase(),void 0===C.bondTable[i]?i=i[0]:e&&("Ca"===i||"Cd"===i)&&(i="C")),i}function M(t){for(const e in t)return!1;return!0}function z(t,e,i){const r=[],s=void 0===e.assignBonds||e.assignBonds,a=!e.keepH,o=!!e.noSecondaryStructure,l=!e.noComputeSecondaryStructure,h=!e.doAssembly,c=e.altLoc?e.altLoc:"A",d={symmetries:[],cryst:void 0};let u,f=[];const p=[];let g;const _={};for(let e=0;e=4?1:i}}else _[a]=1,0!=e.bonds.length&&e.bonds[e.bonds.length-1]===n||(e.bonds.push(n),e.bondOrder.push(1))}}else if("HELIX "===s){o=g.substring(19,20),l=parseInt(g.substring(21,25)),m=parseInt(g.substring(33,37)),o in i||(i[o]={}),i[o][l]="h1";for(let t=l+1;t1&&("1"==t[1]?e.ssbegin=!0:"2"==t[1]&&(e.ssend=!0))}}return[r,d,f]}function T(t,e){e=e||{};var i=[],r={};i.modelData=[];for(var s=t.split(/\r?\n|\r/);s.length>0;){var n=z(s,e,r),a=n[0],o=n[1];if(s=n[2],0!=a.length){if(e.multimodel&&e.onemol&&i.length>0)for(var l=i[0].length,h=0;h1&&e[1].toUpperCase()!=e[1]&&(g=e.substring(0,2)),l="H"==s[0],n[t]=i[i.length-1].length,i[i.length-1].push({resn:r,x:c,y:d,z:u,elem:g,hetflag:l,chain:a,resi:o,serial:t,atom:e,bonds:[],ss:"c",bondOrder:[],properties:{charge:f,partialCharge:f,radius:p},pdbline:s})}else if("CONECT"==o){var v=parseInt(s.substring(6,11)),_=i[i.length-1][n[v]];for(let t=0;t<4;t++){var y=parseInt(s.substring([11,16,21,26][t],[11,16,21,26][t]+5)),x=i[i.length-1][n[y]];void 0!==_&&void 0!==x&&(_.bonds.push(n[y]),_.bondOrder.push(1))}}}for(let t=0;t0){var R=g.bioAssemblyList[l].transformList;for(h=0,p=R.length;h{t.chainIndexList.forEach((e=>{N[e]="polymer"==t.type}))}));var G=0;for(f=0;f=A.length||O(A[et]!=tt))&&(J=!0)}var it=g.groupIdList[x],rt=X.groupName;let t=X.chemCompType;var st=w;let e=D.has(t)||!N[b];for(d=0;d=w){G=t;break}let s=H[e],n=H[i],a=j[s],o=j[n];a&&o&&(a.bonds.push(n),a.bondOrder.push(r),o.bonds.push(s),o.bondOrder.push(r))}e.multimodel&&(e.onemol||_.push([]))}if(!o)for(let t=0;t<_.length;t++)v(y[t].symmetries,_[t],e,y[t].cryst);return a&&!s&&m(_,e.hbondCutoff),_}function R(t){var e,i=[],r=0,s=t.split(/\r?\n|\r/);if(!(s.length>0&&s[0].includes("VERSION")))return[];var n=s.filter((function(t){return t.includes("POINTERS")||t.includes("ATOM_NAME")||t.includes("CHARGE")||t.includes("RADII")||t.includes("BONDS_INC_HYDROGEN")||t.includes("BONDS_WITHOUT_HYDROGEN")})),a=c("POINTERS");if(-1==a)return[];var o=d(a),l=parseInt(s[a+1].slice(0,o[1]));if(isNaN(l)||l<=0)return[];if(-1==(a=c("ATOM_NAME")))return[];var h=(o=d(a))[0];for(let t=0;t0){for(;!s[e].includes("FORMAT");)e++;return e}return-1}function d(t){var e=s[t].match(/\((\d*)\S*/),i=s[t].match(/[a-zA-Z](\d*)\)\s*/);return null==i&&(i=s[t].match(/[a-zA-Z](\d*)\.\d*\)\s*/)),[e[1],i[1]]}return[i]}function P(t,e){const i=[],r=t.split(/\r?\n|\r/);for(;r.length>0;){const t=parseInt(r[1]);if(r.length<3||isNaN(t)||t<=0||r.length44&&(n.dx=10*parseFloat(i.slice(44,52)),n.dy=10*parseFloat(i.slice(52,60)),n.dz=10*parseFloat(i.slice(60,68))),e[t]=n}if(r.length<=s+3){const t=r[s++].trim().split(/\s+/);if(3===t.length){for(let e=0;e<3;e++)t[e]=(10*parseFloat(t[e])).toString();i.box=t}}r.splice(0,++s)}for(let t=0;t=0;i--)e=j(e,t.encoding[i]);return e}function j(t,e){switch(e.kind){case"ByteArray":switch(e.type){case N.IntDataType.Uint8:return t;case N.IntDataType.Int8:return function(t){return new Int8Array(t.buffer,t.byteOffset)}(t);case N.IntDataType.Int16:return function(t){return q(t,2,Int16Array)}(t);case N.IntDataType.Uint16:return function(t){return q(t,2,Uint16Array)}(t);case N.IntDataType.Int32:return function(t){return q(t,4,Int32Array)}(t);case N.IntDataType.Uint32:return function(t){return q(t,4,Uint32Array)}(t);case N.FloatDataType.Float32:return function(t){return q(t,4,Float32Array)}(t);case N.FloatDataType.Float64:return function(t){return q(t,8,Float64Array)}(t);default:throw new Error("unreachable")}case"FixedPoint":return function(t,e){const i=t.length,r=W(e.srcType,i),s=1/e.factor;for(let e=0;et.name)),getField(t){const r=e[t];if(r)return i[t]||(i[t]=V(r.data)),i[t]}}}var Z=i(471);class Connectivity{constructor(t){if(this.C={},t){let e=t.getField("comp_id"),i=t.getField("atom_id_1"),r=t.getField("atom_id_2"),s=t.getField("value_order");for(let t=0;t0&&(n.bonds.push(a.index),a.bonds.push(n.index),n.bondOrder.push(o),a.bondOrder.push(o))}}for(let t of e.C){let e=t[0],i=t[1],r=t[2],s=this.geta(e),n=this.geta(i);null!=s&&null!=n&&(s.bonds.push(n.index),n.bonds.push(s.index),s.bondOrder.push(r),n.bondOrder.push(r))}}}function X(t,e){var i=!e.keepH,r=e.altLoc?e.altLoc:"A",s=!e.noComputeSecondaryStructure;const a=!e.doAssembly,o=void 0===e.assignBonds||e.assignBonds;if("string"==typeof t)try{t=(0,L.base64ToArray)(t)}catch(e){t=(new TextEncoder).encode(t)}else t=new Uint8Array(t);var l=Z.decodeMsgpack(t);31==l&&(t=(0,L.inflateString)(t,!1),l=Z.decodeMsgpack(t));var h=[],c=h.modelData=[],d=l.dataBlocks.length;if(0==d)return h;e.multimodel||(d=1);for(let t=0;t1&&("1"==t[1]?i.ssbegin=!0:"2"==t[1]&&(i.ssend=!0))}}}e.multimodel&&t=0||window.navigator.userAgent.indexOf("Trident/")>=0)&&(tt=!0);class MarchingCubeInitializer{constructor(){this.ISDONE=2,this.edgeTable=new Uint32Array([0,0,0,0,0,0,0,2816,0,0,0,1792,0,3328,3584,3840,0,0,0,138,0,21,0,134,0,0,0,652,0,2067,3865,3600,0,0,0,42,0,0,0,294,0,0,21,28,0,3875,1049,3360,0,168,162,170,0,645,2475,2210,0,687,293,172,4010,3747,3497,3232,0,0,0,0,0,69,0,900,0,0,0,1792,138,131,1608,1920,0,81,0,2074,84,85,84,86,0,81,0,3676,330,1105,1881,1616,0,0,0,42,0,69,0,502,0,0,21,3580,138,2035,1273,1520,2816,104,2337,106,840,581,367,102,2816,3695,3429,3180,1898,1635,1385,1120,0,0,0,0,0,0,0,3910,0,0,69,588,42,2083,41,2880,0,0,0,1722,0,2293,4095,3830,0,255,757,764,2538,2291,3065,2800,0,0,81,338,0,3925,1119,3414,84,855,85,340,2130,2899,89,2384,1792,712,194,1162,4036,3781,3535,3270,708,719,197,204,3018,2755,2505,2240,0,0,0,0,168,420,168,1958,162,162,676,2988,170,163,680,928,3328,3096,3328,3642,52,53,1855,1590,2340,2111,2869,2620,298,51,825,560,3584,3584,3090,3482,1668,1941,1183,1430,146,2975,2069,2460,154,915,153,400,3840,3592,3329,3082,1796,1541,1295,1030,2818,2575,2309,2060,778,515,265,0]),this.triTable=[[],[],[],[],[],[],[],[11,9,8],[],[],[],[8,10,9],[],[10,8,11],[9,11,10],[8,10,9,8,11,10],[],[],[],[1,7,3],[],[4,2,0],[],[2,1,7],[],[],[],[2,7,3,2,9,7],[],[1,4,11,1,0,4],[3,8,0,11,9,4,11,10,9],[4,11,9,11,10,9],[],[],[],[5,3,1],[],[],[],[2,5,8,2,1,5],[],[],[2,4,0],[3,2,4],[],[0,9,1,8,10,5,8,11,10],[3,4,0,3,10,4],[5,8,10,8,11,10],[],[3,5,7],[7,1,5],[1,7,3,1,5,7],[],[9,2,0,9,7,2],[0,3,8,1,7,11,1,5,7],[11,1,7,1,5,7],[],[9,1,0,5,3,2,5,7,3],[8,2,5,8,0,2],[2,5,3,5,7,3],[3,9,1,3,8,9,7,11,10,7,10,5],[9,1,0,10,7,11,10,5,7],[3,8,0,7,10,5,7,11,10],[11,5,7,11,10,5],[],[],[],[],[],[0,6,2],[],[7,2,9,7,9,8],[],[],[],[8,10,9],[7,1,3],[7,1,0],[6,9,3,6,10,9],[7,10,8,10,9,8],[],[6,0,4],[],[11,1,4,11,3,1],[2,4,6],[2,0,4,2,4,6],[2,4,6],[1,4,2,4,6,2],[],[6,0,4],[],[2,11,3,6,9,4,6,10,9],[8,6,1,8,1,3],[10,0,6,0,4,6],[8,0,3,9,6,10,9,4,6],[10,4,6,10,9,4],[],[],[],[5,3,1],[],[0,6,2],[],[7,4,8,5,2,1,5,6,2],[],[],[2,4,0],[7,4,8,2,11,3,10,5,6],[7,1,3],[5,6,10,0,9,1,8,7,4],[5,6,10,7,0,3,7,4,0],[10,5,6,4,8,7],[9,11,8],[3,5,6],[0,5,11,0,11,8],[6,3,5,3,1,5],[3,9,6,3,8,9],[9,6,0,6,2,0],[0,3,8,2,5,6,2,1,5],[1,6,2,1,5,6],[9,11,8],[1,0,9,6,10,5,11,3,2],[6,10,5,2,8,0,2,11,8],[3,2,11,10,5,6],[10,5,6,9,3,8,9,1,3],[0,9,1,5,6,10],[8,0,3,10,5,6],[10,5,6],[],[],[],[],[],[],[],[1,10,2,9,11,6,9,8,11],[],[],[6,0,2],[3,6,9,3,2,6],[3,5,1],[0,5,1,0,11,5],[0,3,5],[6,9,11,9,8,11],[],[],[],[4,5,9,7,1,10,7,3,1],[],[11,6,7,2,4,5,2,0,4],[11,6,7,8,0,3,1,10,2,9,4,5],[6,7,11,1,10,2,9,4,5],[],[4,1,0,4,5,1,6,7,3,6,3,2],[9,4,5,0,6,7,0,2,6],[4,5,9,6,3,2,6,7,3],[6,7,11,5,3,8,5,1,3],[6,7,11,4,1,0,4,5,1],[4,5,9,3,8,0,11,6,7],[9,4,5,7,11,6],[],[],[0,6,4],[8,6,4,8,1,6],[],[0,10,2,0,9,10,4,8,11,4,11,6],[10,2,1,6,0,3,6,4,0],[10,2,1,11,4,8,11,6,4],[4,2,6],[1,0,9,2,4,8,2,6,4],[2,4,0,2,6,4],[8,2,4,2,6,4],[11,4,1,11,6,4],[0,9,1,4,11,6,4,8,11],[3,6,0,6,4,0],[8,6,4,8,11,6],[10,8,9],[6,3,9,6,7,3],[6,7,1],[10,7,1,7,3,1],[7,11,6,8,10,2,8,9,10],[11,6,7,10,0,9,10,2,0],[2,1,10,7,11,6,8,0,3],[1,10,2,6,7,11],[7,2,6,7,9,2],[1,0,9,3,6,7,3,2,6],[7,0,6,0,2,6],[2,7,3,2,6,7],[7,11,6,3,9,1,3,8,9],[9,1,0,11,6,7],[0,3,8,11,6,7],[11,6,7],[],[],[],[],[5,3,7],[8,5,2,8,7,5],[5,3,7],[1,10,2,5,8,7,5,9,8],[1,7,5],[1,7,5],[9,2,7,9,7,5],[11,3,2,8,5,9,8,7,5],[1,3,7,1,7,5],[0,7,1,7,5,1],[9,3,5,3,7,5],[9,7,5,9,8,7],[8,10,11],[3,4,10,3,10,11],[8,10,11],[5,9,4,1,11,3,1,10,11],[2,4,5],[5,2,4,2,0,4],[0,3,8,5,9,4,10,2,1],[2,1,10,9,4,5],[2,8,5,2,11,8],[3,2,11,1,4,5,1,0,4],[9,4,5,8,2,11,8,0,2],[11,3,2,9,4,5],[8,5,3,5,1,3],[5,0,4,5,1,0],[3,8,0,4,5,9],[9,4,5],[11,9,10],[11,9,10],[1,11,4,1,10,11],[8,7,4,11,1,10,11,3,1],[2,7,9,2,9,10],[4,8,7,0,10,2,0,9,10],[2,1,10,0,7,4,0,3,7],[10,2,1,8,7,4],[1,7,4],[3,2,11,4,8,7,9,1,0],[11,4,2,4,0,2],[2,11,3,7,4,8],[4,1,7,1,3,7],[1,0,9,8,7,4],[3,4,0,3,7,4],[8,7,4],[8,9,10,8,10,11],[3,9,11,9,10,11],[0,10,8,10,11,8],[10,3,1,10,11,3],[2,8,10,8,9,10],[9,2,0,9,10,2],[8,0,3,1,10,2],[10,2,1],[1,11,9,11,8,9],[11,3,2,0,9,1],[11,0,2,11,8,0],[11,3,2],[8,1,3,8,9,1],[9,1,0],[8,0,3],[]],this.edgeTable2=[0,265,515,778,2060,2309,2575,2822,1030,1295,1541,1804,3082,3331,3593,3840,400,153,915,666,2460,2197,2975,2710,1430,1183,1941,1692,3482,3219,3993,3728,560,825,51,314,2620,2869,2111,2358,1590,1855,1077,1340,3642,3891,3129,3376,928,681,419,170,2988,2725,2479,2214,1958,1711,1445,1196,4010,3747,3497,3232,2240,2505,2755,3018,204,453,719,966,3270,3535,3781,4044,1226,1475,1737,1984,2384,2137,2899,2650,348,85,863,598,3414,3167,3925,3676,1370,1107,1881,1616,2800,3065,2291,2554,764,1013,255,502,3830,4095,3317,3580,1786,2035,1273,1520,2912,2665,2403,2154,876,613,367,102,3942,3695,3429,3180,1898,1635,1385,1120,1120,1385,1635,1898,3180,3429,3695,3942,102,367,613,876,2154,2403,2665,2912,1520,1273,2035,1786,3580,3317,4095,3830,502,255,1013,764,2554,2291,3065,2800,1616,1881,1107,1370,3676,3925,3167,3414,598,863,85,348,2650,2899,2137,2384,1984,1737,1475,1226,4044,3781,3535,3270,966,719,453,204,3018,2755,2505,2240,3232,3497,3747,4010,1196,1445,1711,1958,2214,2479,2725,2988,170,419,681,928,3376,3129,3891,3642,1340,1077,1855,1590,2358,2111,2869,2620,314,51,825,560,3728,3993,3219,3482,1692,1941,1183,1430,2710,2975,2197,2460,666,915,153,400,3840,3593,3331,3082,1804,1541,1295,1030,2822,2575,2309,2060,778,515,265,0],this.triTable2=[[],[8,3,0],[9,0,1],[8,3,1,8,1,9],[11,2,3],[11,2,0,11,0,8],[11,2,3,0,1,9],[2,1,11,1,9,11,11,9,8],[10,1,2],[8,3,0,1,2,10],[9,0,2,9,2,10],[3,2,8,2,10,8,8,10,9],[10,1,3,10,3,11],[1,0,10,0,8,10,10,8,11],[0,3,9,3,11,9,9,11,10],[8,10,9,8,11,10],[8,4,7],[3,0,4,3,4,7],[1,9,0,8,4,7],[9,4,1,4,7,1,1,7,3],[2,3,11,7,8,4],[7,11,4,11,2,4,4,2,0],[3,11,2,4,7,8,9,0,1],[2,7,11,2,1,7,1,4,7,1,9,4],[10,1,2,8,4,7],[2,10,1,0,4,7,0,7,3],[4,7,8,0,2,10,0,10,9],[2,7,3,2,9,7,7,9,4,2,10,9],[8,4,7,11,10,1,11,1,3],[11,4,7,1,4,11,1,11,10,1,0,4],[3,8,0,7,11,4,11,9,4,11,10,9],[7,11,4,4,11,9,11,10,9],[9,5,4],[3,0,8,4,9,5],[5,4,0,5,0,1],[4,8,5,8,3,5,5,3,1],[11,2,3,9,5,4],[9,5,4,8,11,2,8,2,0],[3,11,2,1,5,4,1,4,0],[8,5,4,2,5,8,2,8,11,2,1,5],[2,10,1,9,5,4],[0,8,3,5,4,9,10,1,2],[10,5,2,5,4,2,2,4,0],[3,4,8,3,2,4,2,5,4,2,10,5],[5,4,9,1,3,11,1,11,10],[0,9,1,4,8,5,8,10,5,8,11,10],[3,4,0,3,10,4,4,10,5,3,11,10],[4,8,5,5,8,10,8,11,10],[9,5,7,9,7,8],[0,9,3,9,5,3,3,5,7],[8,0,7,0,1,7,7,1,5],[1,7,3,1,5,7],[11,2,3,8,9,5,8,5,7],[9,2,0,9,7,2,2,7,11,9,5,7],[0,3,8,2,1,11,1,7,11,1,5,7],[2,1,11,11,1,7,1,5,7],[1,2,10,5,7,8,5,8,9],[9,1,0,10,5,2,5,3,2,5,7,3],[5,2,10,8,2,5,8,5,7,8,0,2],[10,5,2,2,5,3,5,7,3],[3,9,1,3,8,9,7,11,10,7,10,5],[9,1,0,10,7,11,10,5,7],[3,8,0,7,10,5,7,11,10],[11,5,7,11,10,5],[11,7,6],[0,8,3,11,7,6],[9,0,1,11,7,6],[7,6,11,3,1,9,3,9,8],[2,3,7,2,7,6],[8,7,0,7,6,0,0,6,2],[1,9,0,3,7,6,3,6,2],[7,6,2,7,2,9,2,1,9,7,9,8],[1,2,10,6,11,7],[2,10,1,7,6,11,8,3,0],[11,7,6,10,9,0,10,0,2],[7,6,11,3,2,8,8,2,10,8,10,9],[6,10,7,10,1,7,7,1,3],[6,10,1,6,1,7,7,1,0,7,0,8],[9,0,3,6,9,3,6,10,9,6,3,7],[6,10,7,7,10,8,10,9,8],[8,4,6,8,6,11],[11,3,6,3,0,6,6,0,4],[0,1,9,4,6,11,4,11,8],[1,9,4,11,1,4,11,3,1,11,4,6],[3,8,2,8,4,2,2,4,6],[2,0,4,2,4,6],[1,9,0,3,8,2,2,8,4,2,4,6],[9,4,1,1,4,2,4,6,2],[10,1,2,11,8,4,11,4,6],[10,1,2,11,3,6,6,3,0,6,0,4],[0,2,10,0,10,9,4,11,8,4,6,11],[2,11,3,6,9,4,6,10,9],[8,4,6,8,6,1,6,10,1,8,1,3],[1,0,10,10,0,6,0,4,6],[8,0,3,9,6,10,9,4,6],[10,4,6,10,9,4],[9,5,4,7,6,11],[4,9,5,3,0,8,11,7,6],[6,11,7,4,0,1,4,1,5],[6,11,7,4,8,5,5,8,3,5,3,1],[4,9,5,6,2,3,6,3,7],[9,5,4,8,7,0,0,7,6,0,6,2],[4,0,1,4,1,5,6,3,7,6,2,3],[7,4,8,5,2,1,5,6,2],[6,11,7,1,2,10,9,5,4],[11,7,6,8,3,0,1,2,10,9,5,4],[11,7,6,10,5,2,2,5,4,2,4,0],[7,4,8,2,11,3,10,5,6],[4,9,5,6,10,7,7,10,1,7,1,3],[5,6,10,0,9,1,8,7,4],[5,6,10,7,0,3,7,4,0],[10,5,6,4,8,7],[5,6,9,6,11,9,9,11,8],[0,9,5,0,5,3,3,5,6,3,6,11],[0,1,5,0,5,11,5,6,11,0,11,8],[11,3,6,6,3,5,3,1,5],[9,5,6,3,9,6,3,8,9,3,6,2],[5,6,9,9,6,0,6,2,0],[0,3,8,2,5,6,2,1,5],[1,6,2,1,5,6],[1,2,10,5,6,9,9,6,11,9,11,8],[1,0,9,6,10,5,11,3,2],[6,10,5,2,8,0,2,11,8],[3,2,11,10,5,6],[10,5,6,9,3,8,9,1,3],[0,9,1,5,6,10],[8,0,3,10,5,6],[10,5,6],[10,6,5],[8,3,0,10,6,5],[0,1,9,5,10,6],[10,6,5,9,8,3,9,3,1],[3,11,2,10,6,5],[6,5,10,2,0,8,2,8,11],[1,9,0,6,5,10,11,2,3],[1,10,2,5,9,6,9,11,6,9,8,11],[1,2,6,1,6,5],[0,8,3,2,6,5,2,5,1],[5,9,6,9,0,6,6,0,2],[9,6,5,3,6,9,3,9,8,3,2,6],[11,6,3,6,5,3,3,5,1],[0,5,1,0,11,5,5,11,6,0,8,11],[0,5,9,0,3,5,3,6,5,3,11,6],[5,9,6,6,9,11,9,8,11],[10,6,5,4,7,8],[5,10,6,7,3,0,7,0,4],[5,10,6,0,1,9,8,4,7],[4,5,9,6,7,10,7,1,10,7,3,1],[7,8,4,2,3,11,10,6,5],[11,6,7,10,2,5,2,4,5,2,0,4],[11,6,7,8,0,3,1,10,2,9,4,5],[6,7,11,1,10,2,9,4,5],[7,8,4,5,1,2,5,2,6],[4,1,0,4,5,1,6,7,3,6,3,2],[9,4,5,8,0,7,0,6,7,0,2,6],[4,5,9,6,3,2,6,7,3],[6,7,11,4,5,8,5,3,8,5,1,3],[6,7,11,4,1,0,4,5,1],[4,5,9,3,8,0,11,6,7],[9,4,5,7,11,6],[10,6,4,10,4,9],[8,3,0,9,10,6,9,6,4],[1,10,0,10,6,0,0,6,4],[8,6,4,8,1,6,6,1,10,8,3,1],[2,3,11,6,4,9,6,9,10],[0,10,2,0,9,10,4,8,11,4,11,6],[10,2,1,11,6,3,6,0,3,6,4,0],[10,2,1,11,4,8,11,6,4],[9,1,4,1,2,4,4,2,6],[1,0,9,3,2,8,2,4,8,2,6,4],[2,4,0,2,6,4],[3,2,8,8,2,4,2,6,4],[1,4,9,11,4,1,11,1,3,11,6,4],[0,9,1,4,11,6,4,8,11],[11,6,3,3,6,0,6,4,0],[8,6,4,8,11,6],[6,7,10,7,8,10,10,8,9],[9,3,0,6,3,9,6,9,10,6,7,3],[6,1,10,6,7,1,7,0,1,7,8,0],[6,7,10,10,7,1,7,3,1],[7,11,6,3,8,2,8,10,2,8,9,10],[11,6,7,10,0,9,10,2,0],[2,1,10,7,11,6,8,0,3],[1,10,2,6,7,11],[7,2,6,7,9,2,2,9,1,7,8,9],[1,0,9,3,6,7,3,2,6],[8,0,7,7,0,6,0,2,6],[2,7,3,2,6,7],[7,11,6,3,9,1,3,8,9],[9,1,0,11,6,7],[0,3,8,11,6,7],[11,6,7],[11,7,5,11,5,10],[3,0,8,7,5,10,7,10,11],[9,0,1,10,11,7,10,7,5],[3,1,9,3,9,8,7,10,11,7,5,10],[10,2,5,2,3,5,5,3,7],[5,10,2,8,5,2,8,7,5,8,2,0],[9,0,1,10,2,5,5,2,3,5,3,7],[1,10,2,5,8,7,5,9,8],[2,11,1,11,7,1,1,7,5],[0,8,3,2,11,1,1,11,7,1,7,5],[9,0,2,9,2,7,2,11,7,9,7,5],[11,3,2,8,5,9,8,7,5],[1,3,7,1,7,5],[8,7,0,0,7,1,7,5,1],[0,3,9,9,3,5,3,7,5],[9,7,5,9,8,7],[4,5,8,5,10,8,8,10,11],[3,0,4,3,4,10,4,5,10,3,10,11],[0,1,9,4,5,8,8,5,10,8,10,11],[5,9,4,1,11,3,1,10,11],[3,8,4,3,4,2,2,4,5,2,5,10],[10,2,5,5,2,4,2,0,4],[0,3,8,5,9,4,10,2,1],[2,1,10,9,4,5],[8,4,5,2,8,5,2,11,8,2,5,1],[3,2,11,1,4,5,1,0,4],[9,4,5,8,2,11,8,0,2],[11,3,2,9,4,5],[4,5,8,8,5,3,5,1,3],[5,0,4,5,1,0],[3,8,0,4,5,9],[9,4,5],[7,4,11,4,9,11,11,9,10],[3,0,8,7,4,11,11,4,9,11,9,10],[11,7,4,1,11,4,1,10,11,1,4,0],[8,7,4,11,1,10,11,3,1],[2,3,7,2,7,9,7,4,9,2,9,10],[4,8,7,0,10,2,0,9,10],[2,1,10,0,7,4,0,3,7],[10,2,1,8,7,4],[2,11,7,2,7,1,1,7,4,1,4,9],[3,2,11,4,8,7,9,1,0],[7,4,11,11,4,2,4,0,2],[2,11,3,7,4,8],[9,1,4,4,1,7,1,3,7],[1,0,9,8,7,4],[3,4,0,3,7,4],[8,7,4],[8,9,10,8,10,11],[0,9,3,3,9,11,9,10,11],[1,10,0,0,10,8,10,11,8],[10,3,1,10,11,3],[3,8,2,2,8,10,8,9,10],[9,2,0,9,10,2],[8,0,3,1,10,2],[10,2,1],[2,11,1,1,11,9,11,8,9],[11,3,2,0,9,1],[11,0,2,11,8,0],[11,3,2],[8,1,3,8,9,1],[9,1,0],[8,0,3],[]]}march(t,e,i,r){let s=!!r.fulltable,n=r.hasOwnProperty("origin")&&r.origin.hasOwnProperty("x")?r.origin:{x:0,y:0,z:0},a=!!r.voxel,o=r.matrix,l=r.nX||0,h=r.nY||0,c=r.nZ||0,d=r.scale||1,u=null;u=r.unitCube?r.unitCube:{x:d,y:d,z:d};let f,p,g=new Int32Array(l*h*c);for(f=0,p=g.length;f>2))+r+((2&e)>>1))*c+s+(1&e)]&this.ISDONE)<=3&&(e.push(e[r]),r=e.length-1,e.push(e[s]),s=e.length-1,e.push(e[n]),n=e.length-1),i.push(r),i.push(s),i.push(n)}}}laplacianSmooth(t,e,i){let r,s,n,a,o,l=new Array(e.length);for(r=0,s=e.length;r1e6&&(this.scaleFactor=this.defaultScaleFactor/2);let r=1/this.scaleFactor*5.5;this.pminx=t[0][0],this.pmaxx=t[1][0],this.pminy=t[0][1],this.pmaxy=t[1][1],this.pminz=t[0][2],this.pmaxz=t[1][2],e?(this.pminx-=this.probeRadius+r,this.pminy-=this.probeRadius+r,this.pminz-=this.probeRadius+r,this.pmaxx+=this.probeRadius+r,this.pmaxy+=this.probeRadius+r,this.pmaxz+=this.probeRadius+r):(this.pminx-=r,this.pminy-=r,this.pminz-=r,this.pmaxx+=r,this.pmaxy+=r,this.pmaxz+=r),this.pminx=Math.floor(this.pminx*this.scaleFactor)/this.scaleFactor,this.pminy=Math.floor(this.pminy*this.scaleFactor)/this.scaleFactor,this.pminz=Math.floor(this.pminz*this.scaleFactor)/this.scaleFactor,this.pmaxx=Math.ceil(this.pmaxx*this.scaleFactor)/this.scaleFactor,this.pmaxy=Math.ceil(this.pmaxy*this.scaleFactor)/this.scaleFactor,this.pmaxz=Math.ceil(this.pmaxz*this.scaleFactor)/this.scaleFactor,this.ptranx=-this.pminx,this.ptrany=-this.pminy,this.ptranz=-this.pminz,this.pLength=Math.ceil(this.scaleFactor*(this.pmaxx-this.pminx))+1,this.pWidth=Math.ceil(this.scaleFactor*(this.pmaxy-this.pminy))+1,this.pHeight=Math.ceil(this.scaleFactor*(this.pmaxz-this.pminz))+1,this.boundingatom(e),this.cutRadius=this.probeRadius*this.scaleFactor,this.vpBits=new Uint8Array(this.pLength*this.pWidth*this.pHeight),this.vpDistance=new Float64Array(this.pLength*this.pWidth*this.pHeight),this.vpAtomID=new Int32Array(this.pLength*this.pWidth*this.pHeight)}boundingatom(t){let e={};for(const i in this.vdwRadii){let r=this.vdwRadii[i];e[i]=t?(r+this.probeRadius)*this.scaleFactor+.5:r*this.scaleFactor+.5;let s=e[i]*e[i];this.widxz[i]=Math.floor(e[i])+1,this.depty[i]=new Int32Array(this.widxz[i]*this.widxz[i]);let n=0;for(let t=0;ts)this.depty[i][n]=-1;else{let t=Math.sqrt(s-r);this.depty[i][n]=Math.floor(t)}n++}}}fillvoxels(t,e){for(let t=0,e=this.vpBits.length;t=this.pLength||h>=this.pWidth||c>=this.pHeight)continue;let u=a*o+h*this.pHeight+c;if(this.vpBits[u]&this.INOUT){let a=e[this.vpAtomID[u]];if(a.serial!=t.serial){let e=i+f-Math.floor(.5+this.scaleFactor*(a.x+this.ptranx)),o=r+n-Math.floor(.5+this.scaleFactor*(a.y+this.ptrany)),l=s+p-Math.floor(.5+this.scaleFactor*(a.z+this.ptranz));f*f+n*n+p*p=this.pLength||h>=this.pWidth||c>=this.pHeight)continue;let u=a*o+h*this.pHeight+c;if(this.vpBits[u]&this.ISDONE){let a=e[this.vpAtomID[u]];if(a.serial!=t.serial){let e=r+f-Math.floor(.5+this.scaleFactor*(a.x+this.ptranx)),o=s+i-Math.floor(.5+this.scaleFactor*(a.y+this.ptrany)),l=n+p-Math.floor(.5+this.scaleFactor*(a.z+this.ptranz));f*f+i*i+p*p-1&&a-1&&l-1&&o=o)||(this.vpBits[t]|=this.ISBOUND))}fastoneshell(t,e){let i,r,s,n,a,o,l,h,c,d=[];if(0===t.length)return d;let u={ix:-1,iy:-1,iz:-1},f=this.pWidth*this.pHeight;for(let p=0,g=t.length;p-1&&u.iy-1&&u.iz-1&&(c=u.ix*f+this.pHeight*u.iy+u.iz,this.vpBits[c]&this.INOUT&&!(this.vpBits[c]&this.ISDONE)?(e.set(u.ix,u.iy,s+this.nb[t][2],h),n=u.ix-h.ix,a=u.iy-h.iy,o=u.iz-h.iz,l=n*n+a*a+o*o,this.vpDistance[c]=l,this.vpBits[c]|=this.ISDONE,this.vpBits[c]|=this.ISBOUND,d.push({ix:u.ix,iy:u.iy,iz:u.iz})):this.vpBits[c]&this.INOUT&&this.vpBits[c]&this.ISDONE&&(n=u.ix-h.ix,a=u.iy-h.iy,o=u.iz-h.iz,l=n*n+a*a+o*o,l-1&&u.iy-1&&u.iz-1&&(c=u.ix*f+this.pHeight*u.iy+u.iz,this.vpBits[c]&this.INOUT&&!(this.vpBits[c]&this.ISDONE)?(e.set(u.ix,u.iy,s+this.nb[t][2],h),n=u.ix-h.ix,a=u.iy-h.iy,o=u.iz-h.iz,l=n*n+a*a+o*o,this.vpDistance[c]=l,this.vpBits[c]|=this.ISDONE,this.vpBits[c]|=this.ISBOUND,d.push({ix:u.ix,iy:u.iy,iz:u.iz})):this.vpBits[c]&this.INOUT&&this.vpBits[c]&this.ISDONE&&(n=u.ix-h.ix,a=u.iy-h.iy,o=u.iz-h.iz,l=n*n+a*a+o*o,l-1&&u.iy-1&&u.iz-1&&(c=u.ix*f+this.pHeight*u.iy+u.iz,this.vpBits[c]&this.INOUT&&!(this.vpBits[c]&this.ISDONE)?(e.set(u.ix,u.iy,s+this.nb[t][2],h),n=u.ix-h.ix,a=u.iy-h.iy,o=u.iz-h.iz,l=n*n+a*a+o*o,this.vpDistance[c]=l,this.vpBits[c]|=this.ISDONE,this.vpBits[c]|=this.ISBOUND,d.push({ix:u.ix,iy:u.iy,iz:u.iz})):this.vpBits[c]&this.INOUT&&this.vpBits[c]&this.ISDONE&&(n=u.ix-h.ix,a=u.iy-h.iy,o=u.iz-h.iz,l=n*n+a*a+o*o,l0){var m=[n,n+1,n-1,n-2],v=d.faceidx;p[v]=m[0],p[v+1]=m[1],p[v+2]=m[3],p[v+3]=m[1],p[v+4]=m[2],p[v+5]=m[3],d.faceidx+=6}d.vertices+=2}}function ut(t,e,i,r,n,a,o){o&&"default"!==o||(o="rectangle"),"edged"===o?function(t,e,i,r,n){if(!(e.length<2)){var a,o;if(a=e[0],o=e[e.length-1],a=nt(a,r),o=nt(o,r),!n)return dt(t,a,o,i);var l,h,c,d,u,f,p,g,m,v,_,y,b,x,w,A,C,S,M,z,T,E,L=[],F=[[0,2,-6,-8],[-4,-2,6,4],[7,-1,-5,3],[-3,5,1,-7]];for(b=0,x=a.length;b0){var O=void 0!==y&&void 0!==_&&y.serial!==_.serial;for(w=0;w<4;w++){var D=[f+F[w][0],f+F[w][1],f+F[w][2],f+F[w][3]];if(E[g=M.faceidx]=D[0],E[g+1]=D[1],E[g+2]=D[3],E[g+3]=D[1],E[g+4]=D[2],E[g+5]=D[3],M.faceidx+=6,_.clickable||y.clickable||_.hoverable||y.hoverable){var k=L[D[3]].clone(),R=L[D[0]].clone(),P=L[D[2]].clone(),U=L[D[1]].clone();if(k.atom=L[D[3]].atom||null,P.atom=L[D[2]].atom||null,R.atom=L[D[0]].atom||null,U.atom=L[D[1]].atom||null,O){var B=k.clone().add(R).multiplyScalar(.5),N=P.clone().add(U).multiplyScalar(.5),G=k.clone().add(U).multiplyScalar(.5);w%2==0?((y.clickable||y.hoverable)&&(A=new J.Triangle(B,G,k),C=new J.Triangle(N,P,G),S=new J.Triangle(G,P,k),y.intersectionShape.triangle.push(A),y.intersectionShape.triangle.push(C),y.intersectionShape.triangle.push(S)),(_.clickable||_.hoverable)&&(A=new J.Triangle(R,U,G),C=new J.Triangle(U,N,G),S=new J.Triangle(R,G,B),_.intersectionShape.triangle.push(A),_.intersectionShape.triangle.push(C),_.intersectionShape.triangle.push(S))):((_.clickable||_.hoverable)&&(A=new J.Triangle(B,G,k),C=new J.Triangle(N,P,G),S=new J.Triangle(G,P,k),_.intersectionShape.triangle.push(A),_.intersectionShape.triangle.push(C),_.intersectionShape.triangle.push(S)),(y.clickable||y.hoverable)&&(A=new J.Triangle(R,U,G),C=new J.Triangle(U,N,G),S=new J.Triangle(R,G,B),y.intersectionShape.triangle.push(A),y.intersectionShape.triangle.push(C),y.intersectionShape.triangle.push(S)))}else(_.clickable||_.hoverable)&&(A=new J.Triangle(R,U,k),C=new J.Triangle(U,P,k),_.intersectionShape.triangle.push(A),_.intersectionShape.triangle.push(C))}}}M.vertices+=8,y=_}var V=L.length-8;for(z=(M=t.updateGeoGroup(8)).vertexArray,T=M.colorArray,E=M.faceArray,p=3*(f=M.vertices),g=M.faceidx,b=0;b<4;b++){L.push(L[2*b]),L.push(L[V+2*b]);var j=L[2*b],H=L[V+2*b];z[p+6*b]=j.x,z[p+1+6*b]=j.y,z[p+2+6*b]=j.z,z[p+3+6*b]=H.x,z[p+4+6*b]=H.y,z[p+5+6*b]=H.z,T[p+6*b]=m.r,T[p+1+6*b]=m.g,T[p+2+6*b]=m.b,T[p+3+6*b]=m.r,T[p+4+6*b]=m.g,T[p+5+6*b]=m.b}V+=8,A=[f,f+2,f+6,f+4],C=[f+1,f+5,f+7,f+3],E[g]=A[0],E[g+1]=A[1],E[g+2]=A[3],E[g+3]=A[1],E[g+4]=A[2],E[g+5]=A[3],E[g+6]=C[0],E[g+7]=C[1],E[g+8]=C[3],E[g+9]=C[1],E[g+10]=C[2],E[g+11]=C[3],M.faceidx+=12,M.vertices+=8}}(t,e,i,r,n):"rectangle"!==o&&"oval"!==o&&"parabola"!==o||function(t,e,i,r,n,a,o){var l,h,c,d,u,f,p,g,m,v;if((c=e.length)<2||e[0].length<2)return;for(l=0;l0&&(l-=1,a=!0),M=Math.round(l*(i.length-1)/d),S=s.CC.color(i[M]),m=p,v=g,p=[],g=[],u=[],void 0!==e[0][l].atom&&(C=e[0][l].atom,"oval"===o?f=_:"rectangle"===o?f=y:"parabola"===o&&(f=b)),f||(f=y),h=0;h0&&!a){for(h=0;h<2*c;h++)L=[x+F[h][0],x+F[h][1],x+F[h][2],x+F[h][3]],E[A=I.faceidx]=L[0],E[A+1]=L[1],E[A+2]=L[3],E[A+3]=L[1],E[A+4]=L[2],E[A+5]=L[3],I.faceidx+=6;if(C.clickable||C.hoverable){var k=[];for(h in k.push(new J.Triangle(m[0],p[0],p[c-1])),k.push(new J.Triangle(m[0],p[c-1],m[c-1])),k.push(new J.Triangle(m[c-1],p[c-1],g[c-1])),k.push(new J.Triangle(m[c-1],g[c-1],v[c-1])),k.push(new J.Triangle(g[0],v[0],v[c-1])),k.push(new J.Triangle(g[c-1],g[0],v[c-1])),k.push(new J.Triangle(p[0],m[0],v[0])),k.push(new J.Triangle(g[0],p[0],v[0])),k)C.intersectionShape.triangle.push(k[h])}}I.vertices+=2*c}for(z=I.vertexArray,T=I.colorArray,E=I.faceArray,w=3*(x=I.vertices),A=I.faceidx,l=0;l=0&&i<1&&(a.transparent=!0,a.opacity=i),a.outline=r;var o=new n.Mesh(e,a);t.add(o)}}function mt(t,e,i,r,s,n,a,o,l){var h,c,d,u,f,p;if(r&&s&&a){var g=s.sub(r);g.normalize();var m=o[l];for(c=l+1;c0&&ut(T,F,E,l,g,0,F.style);var i=[],r=null;if(e){for(m=0;m0&&ut(T,F,E,l,g,0,F.style),F=[],m=0;m0&&e.truncateArrayBuffers(!0,!0)}static updateColor(t,e){var i,r,n;e=e||s.CC.color(e),t.colorsNeedUpdate=!0,e.constructor!==Array&&(i=e.r,r=e.g,n=e.b);for(let s in t.geometryGroups){let a=t.geometryGroups[s],o=a.colorArray;for(let t=0,s=a.vertices;t0?l/t:(t+l)/t}c.multiplyScalar(o);var d=new $.Vector3(r.x,r.y,r.z).add(c),u=c.clone().negate();let f=new $.Vector3(r.x,r.y,r.z);t.intersectionShape.cylinder.push(new J.Cylinder(f,d.clone(),n)),t.intersectionShape.sphere.push(new J.Sphere(f,n));var p=[];p[0]=c.clone(),Math.abs(p[0].x)>1e-4?p[0].y+=1:p[0].x+=1,p[0].cross(c),p[0].normalize(),p[4]=p[0].clone(),p[4].crossVectors(p[0],c),p[4].normalize(),p[8]=p[0].clone().negate(),p[12]=p[4].clone().negate(),p[2]=p[0].clone().add(p[4]).normalize(),p[6]=p[4].clone().add(p[8]).normalize(),p[10]=p[8].clone().add(p[12]).normalize(),p[14]=p[12].clone().add(p[0]).normalize(),p[1]=p[0].clone().add(p[2]).normalize(),p[3]=p[2].clone().add(p[4]).normalize(),p[5]=p[4].clone().add(p[6]).normalize(),p[7]=p[6].clone().add(p[8]).normalize(),p[9]=p[8].clone().add(p[10]).normalize(),p[11]=p[10].clone().add(p[12]).normalize(),p[13]=p[12].clone().add(p[14]).normalize(),p[15]=p[14].clone().add(p[0]).normalize();var g,m,v,_,y,b,x,w,A,C,S,M,z,T,E,L,F,I,O,D,k,R,P=h.vertices,U=h.vertexArray,B=h.faceArray,N=h.normalArray,G=h.lineArray;for(m=0,v=p.length;m0){var W=U[g-3],q=U[g-2],Y=U[g-1],Z=new $.Vector3(W,q,Y),X=new $.Vector3(s.x,s.y,s.z),K=d.clone(),Q=new $.Vector3(H.x,H.y,H.z);t.intersectionShape.triangle.push(new J.Triangle(Q,X,Z)),t.intersectionShape.triangle.push(new J.Triangle(Z.clone(),K,Q.clone()))}}h.vertices+=48,U[g=3*h.vertices]=r.x,U[g+1]=r.y,U[g+2]=r.z,U[g+3]=d.x,U[g+4]=d.y,U[g+5]=d.z,U[g+6]=s.x,U[g+7]=s.y,U[g+8]=s.z,h.vertices+=3;var tt=h.vertices-3,et=h.vertices-2,it=h.vertices-1,rt=3*tt,st=3*et,nt=3*it;for(m=0,v=p.length-1;mo&&(o=c),d>l&&(l=d),u>h&&(h=u)}t.center.set((o+s)/2,(l+n)/2,(h+a)/2),t.radius=t.center.distanceTo({x:o,y:l,z:h}),t.box={min:{x:s,y:n,z:a},max:{x:o,y:l,z:h}}}static addCustomGeo(t,e,i,r,s){var n,a,o,l,h,c,d,u,f,p=e.addGeoGroup(),g=i.vertexArr,m=i.normalArr,v=i.faceArr;p.vertices=g.length,p.faceidx=v.length;var _=p.vertexArray,y=p.colorArray;for(r.constructor!==Array&&(u=r.r,f=r.g,l=r.b),c=0,d=p.vertices;ch?(c.fromCap=0,c.toCap=2):(c.fromCap=2,c.toCap=2),this.addCylinder(c)}}addLine(t){var e,i;e=t.start?new $.Vector3(t.start.x||0,t.start.y||0,t.start.z||0):new $.Vector3(0,0,0),t.end?void 0===(i=new $.Vector3(t.end.x,t.end.y||0,t.end.z||0)).x&&(i.x=3):i=new $.Vector3(3,0,0);var r=this.geo.updateGeoGroup(2),s=r.vertices,n=3*s,a=r.vertexArray;a[n]=e.x,a[n+1]=e.y,a[n+2]=e.z,a[n+3]=i.x,a[n+4]=i.y,a[n+5]=i.z,r.vertices+=2;var o=r.lineArray,l=r.lineidx;o[l]=s,o[l+1]=s+1,r.lineidx+=2;var h=new $.Vector3;this.components.push({centroid:h.addVectors(e,i).multiplyScalar(.5)}),r=this.geo.updateGeoGroup(0),GLShape.updateBoundingFromPoints(this.boundingSphere,this.components,r.vertexArray,r.vertices)}addArrow(t){if(t.start?t.start=new $.Vector3(t.start.x||0,t.start.y||0,t.start.z||0):t.start=new $.Vector3(0,0,0),t.dir instanceof $.Vector3&&"number"==typeof t.length){var e=t.dir.clone().multiplyScalar(t.length).add(t.start);t.end=e}else t.end?(t.end=new $.Vector3(t.end.x,t.end.y||0,t.end.z||0),void 0===t.end.x&&(t.end.x=3)):t.end=new $.Vector3(3,0,0);t.radius=t.radius||.1,t.radiusRatio=t.radiusRatio||1.618034,t.mid=0=0?f[s]-a:a-f[s])>0&&(p[s]|=GLShape.ISDONE)}var g=[],m=[];MarchingCube.march(p,g,m,{fulltable:!0,voxel:o,unitCube:t.unit,origin:t.origin,matrix:t.matrix,nX:h,nY:c,nZ:d}),!o&&l>0&&MarchingCube.laplacianSmooth(l,g,m);var v=[],_=[],y=[];if(e.selectedRegion&&void 0===e.coords&&(e.coords=e.selectedRegion),void 0===e.coords&&void 0!==e.selection&&(r?e.coords=r.selectedAtoms(e.selection):console.log("addIsosurface needs viewer is selection provided.")),void 0!==e.coords){var b=e.coords[0].x,x=e.coords[0].y,w=e.coords[0].z,A=e.coords[0].x,C=e.coords[0].y,S=e.coords[0].z;for(let t=0;tb?b=e.coords[t].x:e.coords[t].xx?x=e.coords[t].y:e.coords[t].yw?w=e.coords[t].z:e.coords[t].zA&&g[t].xC&&g[t].yS&&g[t].z0){var r=new n.LineBasicMaterial({linewidth:this.linewidth,color:this.color}),s=new n.Line(this.linegeo,r,n.LineStyle.LinePieces);this.shapeObj.add(s)}this.renderedShapeObj=this.shapeObj.clone(),t.add(this.renderedShapeObj)}}removegl(t){this.renderedShapeObj&&(void 0!==this.renderedShapeObj.geometry&&this.renderedShapeObj.geometry.dispose(),void 0!==this.renderedShapeObj.material&&this.renderedShapeObj.material.dispose(),t.remove(this.renderedShapeObj),this.renderedShapeObj=null),this.shapeObj=null}get position(){return this.boundingSphere.center}get x(){return this.boundingSphere.center.x}get y(){return this.boundingSphere.center.y}get z(){return this.boundingSphere.center.z}}function wt(t){if(t.vertexArr.length<64e3)return[t];var e=[{vertexArr:[],normalArr:[],faceArr:[]}];t.colorArr&&(e.colorArr=[]);var i=[],r=[],s=0,n=t.faceArr;for(let o=0,l=n.length;o=64e3&&(e.push({vertexArr:[],normalArr:[],faceArr:[]}),t.colorArr&&(e.colorArr=[]),s++)}return e}GLShape.ISDONE=2,GLShape.drawCustom=function(t,e,i){var r=i,n=r.vertexArr,a=r.faceArr;0!==n.length&&0!==a.length||console.warn("Error adding custom shape component: No vertices and/or face indices supplied!");var o=i.color;void 0===o&&(o=t.color),o=s.CC.color(o);for(var l=wt(r),h=0,c=l.length;h=0||t.getIndex(c,d,u)>=0)for(let e=o;e=0&&!i[o]){(e-a.x)*(e-a.x)+(r-a.y)*(r-a.y)+(n-a.z)*(n-a.z){const t=new Uint8Array(4);return!((new Uint32Array(t.buffer)[0]=1)&t[0])})(),Mt={int8:globalThis.Int8Array,uint8:globalThis.Uint8Array,int16:globalThis.Int16Array,uint16:globalThis.Uint16Array,int32:globalThis.Int32Array,uint32:globalThis.Uint32Array,uint64:globalThis.BigUint64Array,int64:globalThis.BigInt64Array,float32:globalThis.Float32Array,float64:globalThis.Float64Array};class IOBuffer{constructor(t=8192,e={}){let i=!1;"number"==typeof t?t=new ArrayBuffer(t):(i=!0,this.lastWrittenByte=t.byteLength);const r=e.offset?e.offset>>>0:0,s=t.byteLength-r;let n=r;(ArrayBuffer.isView(t)||t instanceof IOBuffer)&&(t.byteLength!==t.buffer.byteLength&&(n=t.byteOffset+r),t=t.buffer),this.lastWrittenByte=i?s:0,this.buffer=t,this.length=s,this.byteLength=s,this.byteOffset=n,this.offset=0,this.littleEndian=!0,this._data=new DataView(this.buffer,n,s),this._mark=0,this._marks=[]}available(t=1){return this.offset+t<=this.length}isLittleEndian(){return this.littleEndian}setLittleEndian(){return this.littleEndian=!0,this}isBigEndian(){return!this.littleEndian}setBigEndian(){return this.littleEndian=!1,this}skip(t=1){return this.offset+=t,this}back(t=1){return this.offset-=t,this}seek(t){return this.offset=t,this}mark(){return this._mark=this.offset,this}reset(){return this.offset=this._mark,this}pushMark(){return this._marks.push(this.offset),this}popMark(){const t=this._marks.pop();if(void 0===t)throw new Error("Mark stack empty");return this.seek(t),this}rewind(){return this.offset=0,this}ensureAvailable(t=1){if(!this.available(t)){const e=2*(this.offset+t),i=new Uint8Array(e);i.set(new Uint8Array(this.buffer)),this.buffer=i.buffer,this.length=this.byteLength=e,this._data=new DataView(this.buffer)}return this}readBoolean(){return 0!==this.readUint8()}readInt8(){return this._data.getInt8(this.offset++)}readUint8(){return this._data.getUint8(this.offset++)}readByte(){return this.readUint8()}readBytes(t=1){return this.readArray(t,"uint8")}readArray(t,e){const i=Mt[e].BYTES_PER_ELEMENT*t,r=this.byteOffset+this.offset,s=this.buffer.slice(r,r+i);if(this.littleEndian===St&&"uint8"!==e&&"int8"!==e){const t=new Uint8Array(this.buffer.slice(r,r+i));t.reverse();const s=new Mt[e](t.buffer);return this.offset+=i,s.reverse(),s}const n=new Mt[e](s);return this.offset+=i,n}readInt16(){const t=this._data.getInt16(this.offset,this.littleEndian);return this.offset+=2,t}readUint16(){const t=this._data.getUint16(this.offset,this.littleEndian);return this.offset+=2,t}readInt32(){const t=this._data.getInt32(this.offset,this.littleEndian);return this.offset+=4,t}readUint32(){const t=this._data.getUint32(this.offset,this.littleEndian);return this.offset+=4,t}readFloat32(){const t=this._data.getFloat32(this.offset,this.littleEndian);return this.offset+=4,t}readFloat64(){const t=this._data.getFloat64(this.offset,this.littleEndian);return this.offset+=8,t}readBigInt64(){const t=this._data.getBigInt64(this.offset,this.littleEndian);return this.offset+=8,t}readBigUint64(){const t=this._data.getBigUint64(this.offset,this.littleEndian);return this.offset+=8,t}readChar(){return String.fromCharCode(this.readInt8())}readChars(t=1){let e="";for(let i=0;ithis.lastWrittenByte&&(this.lastWrittenByte=this.offset)}}const zt=1,Tt=2,Et=3,Lt=4,Ft=5,It=6;function Ot(t){switch(Number(t)){case zt:return"byte";case Tt:return"char";case Et:return"short";case Lt:return"int";case Ft:return"float";case It:return"double";default:return"undefined"}}function Dt(t){switch(Number(t)){case zt:case Tt:return 1;case Et:return 2;case Lt:case Ft:return 4;case It:return 8;default:return-1}}function kt(t){switch(String(t)){case"byte":return zt;case"char":return Tt;case"short":return Et;case"int":return Lt;case"float":return Ft;case"double":return It;default:return-1}}function Rt(t,e){if(1!==t){const i=new Array(t);for(let r=0;r6,`non valid type ${c}`);const d=t.readUint32();let u=t.readUint32();2===i&&(Ut(u>0,"offsets larger than 4GB not supported"),u=t.readUint32());let f=!1;void 0!==e&&l[0]===e&&(n+=d,f=!0),s[r]={name:a,dimensions:l,attributes:h,type:Ot(c),size:d,offset:u,record:f}}}return{variables:s,recordStep:n}}(t,r?.id,e);return Array.isArray(n)||(i.variables=n.variables,r.recordStep=n.recordStep),i.recordDimension=r,i}function Vt(t){const e=t.readUint32();let i;if(0===e)return Ut(0!==t.readUint32(),"wrong empty tag for list of attributes"),[];{Ut(12!==e,"wrong tag for list of attributes");const r=t.readUint32();i=new Array(r);for(let e=0;e6,`non valid type ${s}`);const n=t.readUint32(),a=Pt(t,s,n);Bt(t),i[e]={name:r,type:Ot(s),value:a}}}return i}function jt(){const t=[];t.push("DIMENSIONS");for(const e of this.dimensions)t.push(` ${e.name.padEnd(30)} = size: ${e.size}`);t.push(""),t.push("GLOBAL ATTRIBUTES");for(const e of this.globalAttributes)t.push(` ${e.name.padEnd(30)} = ${e.value}`);const e=JSON.parse(JSON.stringify(this.variables));t.push(""),t.push("VARIABLES:");for(const i of e){i.value=this.getDataVariable(i);let e=JSON.stringify(i.value);e.length>50&&(e=e.substring(0,50)),isNaN(i.value.length)||(e+=` (length: ${i.value.length})`),t.push(` ${i.name.padEnd(30)} = ${e}`)}return t.join("\n")}class NetCDFReader{constructor(t){this.toString=jt;const e=new IOBuffer(t);e.setBigEndian(),Ut("CDF"!==e.readChars(3),"should start with CDF");const i=e.readByte();Ut(i>2,"unknown version"),this.header=Gt(e,i),this.buffer=e}get version(){return 1===this.header.version?"classic format":"64-bit offset format"}get recordDimension(){return this.header.recordDimension}get dimensions(){return this.header.dimensions}get globalAttributes(){return this.header.globalAttributes}getAttribute(t){const e=this.globalAttributes.find((e=>e.name===t));return e?e.value:null}getDataVariableAsString(t){const e=this.getDataVariable(t);return e?e.join(""):null}get variables(){return this.header.variables}getDataVariable(t){let e;if(e="string"==typeof t?this.header.variables.find((e=>e.name===t)):t,void 0===e)throw new Error("Not a valid NetCDF v3.x file: variable not found");return this.buffer.seek(e.offset),e.record?function(t,e,i){const r=kt(e.type),s=e.size?e.size/Dt(r):1,n=i.length,a=new Array(n),o=i.recordStep;if(!o)throw new Error("recordDimension.recordStep is undefined");for(let e=0;ee.name===t))}attributeExists(t){return void 0!==this.globalAttributes.find((e=>e.name===t))}}class GLModel{static sameObj(t,e){return t&&e?JSON.stringify(t)==JSON.stringify(e):t==e}constructor(t,e){this.atoms=[],this.frames=[],this.box=null,this.atomdfs=null,this.id=0,this.hidden=!1,this.molObj=null,this.renderedMolObj=null,this.lastColors=null,this.modelData={},this.modelDatas=null,this.idMatrix=new $.Matrix4,this.dontDuplicateAtoms=!0,this.defaultColor=s.elementColors.defaultColor,this.defaultStickRadius=.25,this.options=e||{},this.ElementColors=this.options.defaultcolors?this.options.defaultcolors:s.elementColors.defaultColors,this.defaultSphereRadius=this.options.defaultSphereRadius?this.options.defaultSphereRadius:1.5,this.defaultCartoonQuality=this.options.cartoonQuality?this.options.cartoonQuality:10,this.id=t}getRadiusFromStyle(t,e){var i=this.defaultSphereRadius;if(void 0!==e.radius)i=e.radius;else if(GLModel.vdwRadii[t.elem])i=GLModel.vdwRadii[t.elem];else if(t.elem.length>1){let e=t.elem;e=e[0].toUpperCase()+e[1].toLowerCase(),GLModel.vdwRadii[e]&&(i=GLModel.vdwRadii[e])}return void 0!==e.scale&&(i*=e.scale),i}drawAtomCross(t,e){if(t.style.cross){var i=t.style.cross;if(!i.hidden){var r=i.linewidth||GLModel.defaultlineWidth;e[r]||(e[r]=new n.Geometry);var s=e[r].updateGeoGroup(6),a=this.getRadiusFromStyle(t,i),o=[[a,0,0],[-a,0,0],[0,a,0],[0,-a,0],[0,0,a],[0,0,-a]],l=t.clickable||t.hoverable;l&&void 0===t.intersectionShape&&(t.intersectionShape={sphere:[],cylinder:[],line:[]});for(var h=(0,L.getColorFromStyle)(t,i),c=s.vertexArray,d=s.colorArray,u=0;u<6;u++){var f=3*s.vertices;if(s.vertices++,c[f]=t.x+o[u][0],c[f+1]=t.y+o[u][1],c[f+2]=t.z+o[u][2],d[f]=h.r,d[f+1]=h.g,d[f+2]=h.b,l){var p=new $.Vector3(o[u][0],o[u][1],o[u][2]);p.multiplyScalar(.1),p.set(p.x+t.x,p.y+t.y,p.z+t.z),t.intersectionShape.line.push(p)}}}}}getGoodCross(t,e,i,r){for(var s=null,n=-1,a=0,o=t.bonds.length;an&&(s=c,(n=l)>.1))return s}return s}getSideBondV(t,e,i){var r,s,n,a,o=new $.Vector3(t.x,t.y,t.z),l=new $.Vector3(e.x,e.y,e.z).clone(),h=null;if(l.sub(o),1===t.bonds.length)1===e.bonds.length?(h=l.clone(),Math.abs(h.x)>1e-4?h.y+=1:h.x+=1):(r=(i+1)%e.bonds.length,s=e.bonds[r],(n=this.atoms[s]).index==t.index&&(r=(r+1)%e.bonds.length,s=e.bonds[r],n=this.atoms[s]),(a=new $.Vector3(n.x,n.y,n.z).clone()).sub(o),(h=a.clone()).cross(l));else if((h=this.getGoodCross(t,e,o,l)).lengthSq()<.01){var c=this.getGoodCross(e,t,o,l);null!=c&&(h=c)}return h.lengthSq()<.01&&(h=l.clone(),Math.abs(h.x)>1e-4?h.y+=1:h.x+=1),h.cross(l),h.normalize(),h}addLine(t,e,i,r,s,n){t[i]=r.x,t[i+1]=r.y,t[i+2]=r.z,e[i]=n.r,e[i+1]=n.g,e[i+2]=n.b,t[i+3]=s.x,t[i+4]=s.y,t[i+5]=s.z,e[i+3]=n.r,e[i+4]=n.g,e[i+5]=n.b}drawBondLines(t,e,i){if(t.style.line){var r=t.style.line;if(!r.hidden){var a,o,l,h,c=r.linewidth||GLModel.defaultlineWidth;i[c]||(i[c]=new n.Geometry);for(var d=i[c].updateGeoGroup(6*t.bonds.length),u=d.vertexArray,f=d.colorArray,p=0;p=g.index)){var m=new $.Vector3(t.x,t.y,t.z),v=new $.Vector3(g.x,g.y,g.z),_=m.clone().add(v).multiplyScalar(.5),y=!1,b=t.clickable||t.hoverable,x=g.clickable||g.hoverable;(b||x)&&(b&&(void 0===t.intersectionShape&&(t.intersectionShape={sphere:[],cylinder:[],line:[],triangle:[]}),t.intersectionShape.line.push(m),t.intersectionShape.line.push(_)),x&&(void 0===g.intersectionShape&&(g.intersectionShape={sphere:[],cylinder:[],line:[],triangle:[]}),g.intersectionShape.line.push(_),g.intersectionShape.line.push(v)));var w=(0,L.getColorFromStyle)(t,t.style.line),A=(0,L.getColorFromStyle)(g,g.style.line);if(t.bondStyles&&t.bondStyles[p]){var C=t.bondStyles[p];if(!C.iswire)continue;C.singleBond&&(y=!0),void 0!==C.color1&&(w=s.CC.color(C.color1)),void 0!==C.color2&&(A=s.CC.color(C.color2))}var S,M,z=3*d.vertices;if(t.bondOrder[p]>1&&t.bondOrder[p]<4&&!y){var T=this.getSideBondV(t,g,p),E=v.clone();E.sub(m),2==t.bondOrder[p]?(T.multiplyScalar(.1),(a=m.clone()).add(T),(o=m.clone()).sub(T),(l=a.clone()).add(E),(h=o.clone()).add(E),w==A?(d.vertices+=4,this.addLine(u,f,z,a,l,w),this.addLine(u,f,z+6,o,h,w)):(d.vertices+=8,E.multiplyScalar(.5),(S=a.clone()).add(E),(M=o.clone()).add(E),this.addLine(u,f,z,a,S,w),this.addLine(u,f,z+6,S,l,A),this.addLine(u,f,z+12,o,M,w),this.addLine(u,f,z+18,M,h,A))):3==t.bondOrder[p]&&(T.multiplyScalar(.1),(a=m.clone()).add(T),(o=m.clone()).sub(T),(l=a.clone()).add(E),(h=o.clone()).add(E),w==A?(d.vertices+=6,this.addLine(u,f,z,m,v,w),this.addLine(u,f,z+6,a,l,w),this.addLine(u,f,z+12,o,h,w)):(d.vertices+=12,E.multiplyScalar(.5),(S=a.clone()).add(E),(M=o.clone()).add(E),this.addLine(u,f,z,m,_,w),this.addLine(u,f,z+6,_,v,A),this.addLine(u,f,z+12,a,S,w),this.addLine(u,f,z+18,S,l,A),this.addLine(u,f,z+24,o,M,w),this.addLine(u,f,z+30,M,h,A)))}else w==A?(d.vertices+=2,this.addLine(u,f,z,m,v,w)):(d.vertices+=4,this.addLine(u,f,z,m,_,w),this.addLine(u,f,z+6,_,v,A))}}}}}drawAtomSphere(t,e){if(t.style.sphere){var i=t.style.sphere;if(!i.hidden){var r=(0,L.getColorFromStyle)(t,i),s=this.getRadiusFromStyle(t,i);if((!0===t.clickable||t.hoverable)&&void 0!==t.intersectionShape){var n=new $.Vector3(t.x,t.y,t.z);t.intersectionShape.sphere.push(new J.Sphere(n,s))}rt.drawSphere(e,t,s,r)}}}drawAtomClickSphere(t){if(t.style.clicksphere){var e=t.style.clicksphere;if(!e.hidden){var i=this.getRadiusFromStyle(t,e);if((!0===t.clickable||t.hoverable)&&void 0!==t.intersectionShape){var r=new $.Vector3(t.x,t.y,t.z);t.intersectionShape.sphere.push(new J.Sphere(r,i))}}}}drawAtomInstanced(t,e){if(t.style.sphere){var i=t.style.sphere;if(!i.hidden){var r=this.getRadiusFromStyle(t,i),s=(0,L.getColorFromStyle)(t,i),n=e.updateGeoGroup(1),a=n.vertices,o=3*a,l=n.vertexArray,h=n.colorArray,c=n.radiusArray;if(l[o]=t.x,l[o+1]=t.y,l[o+2]=t.z,h[o]=s.r,h[o+1]=s.g,h[o+2]=s.b,c[a]=r,(!0===t.clickable||t.hoverable)&&void 0!==t.intersectionShape){var d=new $.Vector3(t.x,t.y,t.z);t.intersectionShape.sphere.push(new J.Sphere(d,r))}n.vertices+=1}}}drawSphereImposter(t,e,i,r){var s,n=t.updateGeoGroup(4),a=n.vertices,o=3*a,l=n.vertexArray,h=n.colorArray;for(s=0;s<4;s++)l[o+3*s]=e.x,l[o+3*s+1]=e.y,l[o+3*s+2]=e.z;var c=n.normalArray;for(s=0;s<4;s++)h[o+3*s]=r.r,h[o+3*s+1]=r.g,h[o+3*s+2]=r.b;c[o+0]=-i,c[o+1]=i,c[o+2]=0,c[o+3]=-i,c[o+4]=-i,c[o+5]=0,c[o+6]=i,c[o+7]=-i,c[o+8]=0,c[o+9]=i,c[o+10]=i,c[o+11]=0,n.vertices+=4;var d=n.faceArray,u=n.faceidx;d[u+0]=a,d[u+1]=a+1,d[u+2]=a+2,d[u+3]=a+2,d[u+4]=a+3,d[u+5]=a,n.faceidx+=6}drawAtomImposter(t,e){if(t.style.sphere){var i=t.style.sphere;if(!i.hidden){var r=this.getRadiusFromStyle(t,i),s=(0,L.getColorFromStyle)(t,i);if((!0===t.clickable||t.hoverable)&&void 0!==t.intersectionShape){var n=new $.Vector3(t.x,t.y,t.z);t.intersectionShape.sphere.push(new J.Sphere(n,r))}this.drawSphereImposter(e,t,r,s)}}}calculateDashes(t,e,i,r,s){var n=Math.sqrt(Math.pow(t.x-e.x,2)+Math.pow(t.y-e.y,2)+Math.pow(t.z-e.z,2));i=Math.max(i,0),s=Math.max(s,0)+2*i,(r=Math.max(r,.001))+s>n&&(r=n,s=0);var a,o=Math.floor((n-r)/(r+s))+1;s=(n-o*r)/o;for(var l=new $.Vector3(t.x,t.y,t.z),h=new $.Vector3((e.x-t.x)/(n/s),(e.y-t.y)/(n/s),(e.z-t.z)/(n/s)),c=new $.Vector3((e.x-t.x)/(n/r),(e.y-t.y)/(n/r),(e.z-t.z)/(n/r)),d=[],u=0;u{var e=i.imposter?GLModel.drawStickImposter:rt.drawCylinder;return!T&&t>=1?e:(t,i,r,s,n,a=0,o=0,l=.1,h=.25)=>{this.calculateDashes(i,r,s,l,h).forEach((i=>{e(t,i.from,i.to,s,n,a,o)}))}};for(h=0;h3){if(t.bondOrder[h]<1&&(M*=t.bondOrder[h]),!k.capDrawn&&k.bonds.length<4&&(F=2),I!=P?(_=(new $.Vector3).addVectors(U,B).multiplyScalar(.5),D(i,U,_,M,I,E,0,C,S),D(i,_,B,M,P,0,F,C,S)):D(i,U,B,M,I,E,F,C,S),o=t.clickable||t.hoverable,l=k.clickable||k.hoverable,o||l){if(_||(_=(new $.Vector3).addVectors(U,B).multiplyScalar(.5)),o){var N=new J.Cylinder(U,_,M),G=new J.Sphere(U,M);t.intersectionShape.cylinder.push(N),t.intersectionShape.sphere.push(G)}if(l){var V=new J.Cylinder(B,_,M),j=new J.Sphere(B,M);k.intersectionShape.cylinder.push(V),k.intersectionShape.sphere.push(j)}}}else if(t.bondOrder[h]>1){var H=0,W=0;M!=x&&(H=2,W=2);var q,Y,Z,X,K,Q=B.clone(),tt=null;Q.sub(U),tt=this.getSideBondV(t,k,h),2==t.bondOrder[h]?(q=M*w,tt.multiplyScalar(1.5*q),(Y=U.clone()).add(tt),(Z=U.clone()).sub(tt),(X=Y.clone()).add(Q),(K=Z.clone()).add(Q),I!=P?(_=(new $.Vector3).addVectors(Y,X).multiplyScalar(.5),y=(new $.Vector3).addVectors(Z,K).multiplyScalar(.5),D(i,Y,_,q,I,H,0),D(i,_,X,q,P,0,W),D(i,Z,y,q,I,H,0),D(i,y,K,q,P,0,W)):(D(i,Y,X,q,I,H,W),D(i,Z,K,q,I,H,W)),o=t.clickable||t.hoverable,l=k.clickable||k.hoverable,(o||l)&&(_||(_=(new $.Vector3).addVectors(Y,X).multiplyScalar(.5)),y||(y=(new $.Vector3).addVectors(Z,K).multiplyScalar(.5)),o&&(u=new J.Cylinder(Y,_,q),f=new J.Cylinder(Z,y,q),t.intersectionShape.cylinder.push(u),t.intersectionShape.cylinder.push(f)),l&&(g=new J.Cylinder(X,_,q),m=new J.Cylinder(K,y,q),k.intersectionShape.cylinder.push(g),k.intersectionShape.cylinder.push(m)))):3==t.bondOrder[h]&&(q=M*A,tt.cross(Q),tt.normalize(),tt.multiplyScalar(3*q),(Y=U.clone()).add(tt),(Z=U.clone()).sub(tt),(X=Y.clone()).add(Q),(K=Z.clone()).add(Q),I!=P?(_=(new $.Vector3).addVectors(Y,X).multiplyScalar(.5),y=(new $.Vector3).addVectors(Z,K).multiplyScalar(.5),b=(new $.Vector3).addVectors(U,B).multiplyScalar(.5),D(i,Y,_,q,I,H,0),D(i,_,X,q,P,0,W),D(i,U,b,q,I,E,0),D(i,b,B,q,P,0,F),D(i,Z,y,q,I,H,0),D(i,y,K,q,P,0,W)):(D(i,Y,X,q,I,H,W),D(i,U,B,q,I,E,F),D(i,Z,K,q,I,H,W)),o=t.clickable||t.hoverable,l=k.clickable||k.hoverable,(o||l)&&(_||(_=(new $.Vector3).addVectors(Y,X).multiplyScalar(.5)),y||(y=(new $.Vector3).addVectors(Z,K).multiplyScalar(.5)),b||(b=(new $.Vector3).addVectors(U,B).multiplyScalar(.5)),o&&(u=new J.Cylinder(Y.clone(),_.clone(),q),f=new J.Cylinder(Z.clone(),y.clone(),q),p=new J.Cylinder(U.clone(),b.clone(),q),t.intersectionShape.cylinder.push(u),t.intersectionShape.cylinder.push(f),t.intersectionShape.cylinder.push(p)),l&&(g=new J.Cylinder(X.clone(),_.clone(),q),m=new J.Cylinder(K.clone(),y.clone(),q),v=new J.Cylinder(B.clone(),b.clone(),q),k.intersectionShape.cylinder.push(g),k.intersectionShape.cylinder.push(m),k.intersectionShape.cylinder.push(v))))}}}var et=!1,it=0,st=!1;for(h=0;h0&&(et=!0):0==it&&(t.bonds.length>0||a.showNonBonded)&&(et=!0),et&&(M=x,i.imposter?this.drawSphereImposter(i.sphereGeometry,t,M,I):rt.drawSphere(i,t,M,I))}}}createMolObj(t,e){e=e||{};var i,r,a,o,l=new n.Object3D,h=[],c={},d={},u=this.drawAtomSphere,f=null,p=null;e.supportsImposters?(u=this.drawAtomImposter,(f=new n.Geometry(!0)).imposter=!0,(p=new n.Geometry(!0,!0)).imposter=!0,p.sphereGeometry=new n.Geometry(!0),p.sphereGeometry.imposter=!0,p.drawnCaps={}):e.supportsAIA?(u=this.drawAtomInstanced,(f=new n.Geometry(!1,!0,!0)).instanced=!0,p=new n.Geometry(!0)):(f=new n.Geometry(!0),p=new n.Geometry(!0));var g,m={},v=[Number.POSITIVE_INFINITY,Number.NEGATIVE_INFINITY];for(i=0,a=t.length;iv[1]&&(v[1]=_.resi)),h.push(_))}}if(h.length>0&&xt(l,h,v,this.defaultCartoonQuality),f&&f.vertices>0){f.initTypedArrays();var y=null,b=null;f.imposter?y=new n.SphereImposterMaterial({ambient:0,vertexColors:!0,reflectivity:0}):f.instanced?(b=new n.Geometry(!0),rt.drawSphere(b,{x:0,y:0,z:0},1,new s.Color(.5,.5,.5)),b.initTypedArrays(),y=new n.InstancedMaterial({sphereMaterial:new n.MeshLambertMaterial({ambient:0,vertexColors:!0,reflectivity:0}),sphere:b})):y=new n.MeshLambertMaterial({ambient:0,vertexColors:!0,reflectivity:0}),m.sphere<1&&m.sphere>=0&&(y.transparent=!0,y.opacity=m.sphere),b=new n.Mesh(f,y),l.add(b)}if(p.vertices>0){var x=null,w=null,A=p.sphereGeometry;A&&void 0!==A.vertices&&0!=A.vertices||(A=null),p.initTypedArrays(),A&&A.initTypedArrays();var C={ambient:0,vertexColors:!0,reflectivity:0};p.imposter?(x=new n.StickImposterMaterial(C),w=new n.SphereImposterMaterial(C)):(x=new n.MeshLambertMaterial(C),w=new n.MeshLambertMaterial(C),x.wireframe&&(p.setUpWireframe(),A&&A.setUpWireframe())),m.stick<1&&m.stick>=0&&(x.transparent=!0,x.opacity=m.stick,w.transparent=!0,w.opacity=m.stick);var S=new n.Mesh(p,x);if(l.add(S),A){var M=new n.Mesh(A,w);l.add(M)}}for(i in c)if(c.hasOwnProperty(i)){g=i;var z=new n.LineBasicMaterial({linewidth:g,vertexColors:!0});m.line<1&&m.line>=0&&(z.transparent=!0,z.opacity=m.line),c[i].initTypedArrays();var T=new n.Line(c[i],z,n.LineStyle.LinePieces);l.add(T)}for(i in d)if(d.hasOwnProperty(i)){g=i;var E=new n.LineBasicMaterial({linewidth:g,vertexColors:!0});m.cross<1&&m.cross>=0&&(E.transparent=!0,E.opacity=m.cross),d[i].initTypedArrays();var L=new n.Line(d[i],E,n.LineStyle.LinePieces);l.add(L)}if(this.dontDuplicateAtoms&&this.modelData.symmetries&&this.modelData.symmetries.length>0){var F,I=new n.Object3D;for(F=0;Fi?e-r:e}adjustCoordinatesToBox(){if(this.box&&this.atomdfs)for(var t=this.box[0],e=this.box[1],i=this.box[2],r=.9*t,s=.9*e,n=.9*i,a=0;a=i)&&(t=i-1),null!=r.frames.url){var a=r.frames.url;(0,L.getbin)(a+"/traj/frame/"+t+"/"+r.frames.path,void 0,"POST",void 0).then((function(t){for(var e=new Float32Array(t,44),i=0,n=0;n=r&&t<=s)return!0}}return!1}static deepCopyAndCache(t,e){if("object"!=typeof t||null==t)return t;if(t.__cache_created)return t;const i={};for(const r in t){const s=t[r];if(Array.isArray(s)){i[r]=[];for(let t=0;t=r[0][0]&&a<=r[1][0]&&o>=r[0][1]&&o<=r[1][1]&&l>=r[0][2]&&l<=r[1][2]&&(a>=i[0][0]&&a<=i[1][0]&&o>=i[0][1]&&o<=i[1][1]&&l>=i[0][2]&&l<=i[1][2]||n.push(this.atoms[t]))}return n}static getFloat(t){return"number"==typeof t?t:parseFloat(t)}selectedAtoms(t,e){var i=[];t=GLModel.deepCopyAndCache(t||{},this),e||(e=this.atoms);for(var r=e.length,s=0;s0&&i.push(r[t])}}if(t.hasOwnProperty("within")&&t.within.hasOwnProperty("sel")&&t.within.hasOwnProperty("distance")){var o=this.selectedAtoms(t.within.sel,this.atoms),l={};const e=GLModel.getFloat(t.within.distance),r=e*e;for(let t=0;t0&&(l[e]=1)}var h=[];if(t.within.invert)for(let t=0;t0;)if(e=u.pop(),f=e.chain,p=e.resi,void 0===d[e.index]){d[e.index]=!0;for(var g=0;g0&&(this.molObj=null)}else console.log("Callback is not a function")}setHoverable(t,e,i,r){if(e=!!e,i=(0,L.makeFunction)(i),r=(0,L.makeFunction)(r),null!==i)if(null!==r){var s=this.selectedAtoms(t,this.atoms),n=s.length;for(let t=0;t0&&(this.molObj=null)}else console.log("Unhover_callback is not a function");else console.log("Hover_callback is not a function")}enableContextMenu(t,e){var i;e=!!e;var r=this.selectedAtoms(t,this.atoms),s=r.length;for(i=0;i0&&(this.molObj=null)}setColorByElement(t,e){if(null===this.molObj||!GLModel.sameObj(e,this.lastColors)){this.lastColors=e;var i=this.selectedAtoms(t,i);i.length>0&&(this.molObj=null);for(var r=0;r0&&(this.molObj=null),"string"==typeof i&&void 0!==r.Gradient.builtinGradients[i]&&(i=new r.Gradient.builtinGradients[i]),s||(s=i.range()),s||(s=(0,L.getPropertyRange)(o,e)),n=0;n0&&(this.molObj=null);for(let t=0;t=s)continue;let a={b:i,e:s},o=n.bondOrder[t];1!=o&&(a.o=o),e.b.push(a)}}return e}globj(t,e){(null===this.molObj||e.regen)&&(this.molObj=this.createMolObj(this.atoms,e),this.renderedMolObj&&(t.remove(this.renderedMolObj),this.renderedMolObj=null),this.renderedMolObj=this.molObj.clone(),this.hidden&&(this.renderedMolObj.setVisible(!1),this.molObj.setVisible(!1)),t.add(this.renderedMolObj))}exportVRML(){return this.createMolObj(this.atoms,{supportsImposters:!1,supportsAIA:!1}).vrml()}removegl(t){this.renderedMolObj&&(void 0!==this.renderedMolObj.geometry&&this.renderedMolObj.geometry.dispose(),void 0!==this.renderedMolObj.material&&this.renderedMolObj.material.dispose(),t.remove(this.renderedMolObj),this.renderedMolObj=null),this.molObj=null}hide(){this.hidden=!0,this.renderedMolObj&&this.renderedMolObj.setVisible(!1),this.molObj&&this.molObj.setVisible(!1)}show(){this.hidden=!1,this.renderedMolObj&&this.renderedMolObj.setVisible(!0),this.molObj&&this.molObj.setVisible(!0)}addPropertyLabels(t,e,i,r){for(var s=this.selectedAtoms(e,s),n=(0,L.deepCopy)(r),a=0;aMOLECULE/gm)?"mol2":t.match(/^data_/gm)&&t.match(/^loop_/gm)?"cif":t.match(/^HETATM/gm)||t.match(/^ATOM/gm)?"pdb":t.match(/ITEM: TIMESTEP/gm)?"lammpstrj":t.match(/^.*\n.*\n.\s*(\d+)\s+(\d+)/gm)?"sdf":t.match(/^%VERSION\s+VERSION_STAMP/gm)?"prmtop":"xyz",console.log("Best guess: "+e))),(0,K[e])(t,i)}}GLModel.defaultAtomStyle={line:{}},GLModel.defaultlineWidth=1,GLModel.vdwRadii={H:1.2,He:1.4,Li:1.82,Be:1.53,B:1.92,C:1.7,N:1.55,O:1.52,F:1.47,Ne:1.54,Na:2.27,Mg:1.73,Al:1.84,Si:2.1,P:1.8,S:1.8,Cl:1.75,Ar:1.88,K:2.75,Ca:2.31,Ni:1.63,Cu:1.4,Zn:1.39,Ga:1.87,Ge:2.11,As:1.85,Se:1.9,Br:1.85,Kr:2.02,Rb:3.03,Sr:2.49,Pd:1.63,Ag:1.72,Cd:1.58,In:1.93,Sn:2.17,Sb:2.06,Te:2.06,I:1.98,Xe:2.16,Cs:3.43,Ba:2.68,Pt:1.75,Au:1.66,Hg:1.55,Tl:1.96,Pb:2.02,Bi:2.07,Po:1.97,At:2.02,Rn:2.2,Fr:3.48,Ra:2.83,U:1.86},GLModel.ignoredKeys=new Set(["props","invert","model","frame","byres","expand","within","and","or","not"]);var Ht=i(111);const Wt=16;class GLViewer{getWidth(){let t=this.container,e=t.offsetWidth;if(0==e&&"none"===t.style.display){let i=t.style.position,r=t.style.visibility;t.style.display="block",t.style.visibility="hidden",t.style.position="absolute",e=t.offsetWidth,t.style.display="none",t.style.visibility=r,t.style.position=i}return e}getHeight(){let t=this.container,e=t.offsetHeight;if(0==e&&"none"===t.style.display){let i=t.style.position,r=t.style.visibility;t.style.display="block",t.style.visibility="hidden",t.style.position="absolute",e=t.offsetHeight,t.style.display="none",t.style.visibility=r,t.style.position=i}return e}setupRenderer(){this.renderer=new n.Renderer({antialias:this.config.antialias,preserveDrawingBuffer:!0,premultipliedAlpha:!1,id:this.config.id,row:this.config.row,col:this.config.col,rows:this.config.rows,cols:this.config.cols,canvas:this.config.canvas,containerWidth:this.WIDTH,containerHeight:this.HEIGHT,ambientOcclusion:this.config.ambientOcclusion,outline:this.config.outline}),this.renderer.domElement.style.width="100%",this.renderer.domElement.style.height="100%",this.renderer.domElement.style.padding="0",this.renderer.domElement.style.position="absolute",this.renderer.domElement.style.top="0px",this.renderer.domElement.style.left="0px",this.renderer.domElement.style.zIndex="0"}initializeScene(){this.scene=new n.Scene,this.scene.fog=new n.Fog(this.bgColor,100,200),this.modelGroup=new n.Object3D,this.rotationGroup=new n.Object3D,this.rotationGroup.useQuaternion=!0,this.rotationGroup.quaternion=new $.Quaternion(0,0,0,1),this.rotationGroup.add(this.modelGroup),this.scene.add(this.rotationGroup);var t=new n.Light(16777215);t.position=new $.Vector3(.2,.2,1).normalize(),t.intensity=1,this.scene.add(t)}_handleLostContext(t){const e=function(t){const e=t.getBoundingClientRect();return!(e.right<0||e.bottom<0||e.top>(window.innerHeight||document.documentElement.clientHeight)||e.left>(window.innerWidth||document.documentElement.clientWidth))};if(e(this.container)){let t=0;for(let i of document.getElementsByTagName("canvas"))if(e(i)&&null!=i._3dmol_viewer&&(i._3dmol_viewer.resize(),t+=1,t>=Wt))break}}initContainer(t){this.container=t,this.WIDTH=this.getWidth(),this.HEIGHT=this.getHeight(),this.ASPECT=this.renderer.getAspect(this.WIDTH,this.HEIGHT),this.renderer.setSize(this.WIDTH,this.HEIGHT),this.container.append(this.renderer.domElement),this.glDOM=this.renderer.domElement,this.glDOM._3dmol_viewer=this,this.glDOM.addEventListener("webglcontextlost",this._handleLostContext.bind(this)),this.nomouse||(this.glDOM.addEventListener("mousedown",this._handleMouseDown.bind(this),{passive:!1}),this.glDOM.addEventListener("touchstart",this._handleMouseDown.bind(this),{passive:!1}),this.glDOM.addEventListener("wheel",this._handleMouseScroll.bind(this),{passive:!1}),this.glDOM.addEventListener("mousemove",this._handleMouseMove.bind(this),{passive:!1}),this.glDOM.addEventListener("touchmove",this._handleMouseMove.bind(this),{passive:!1}),this.glDOM.addEventListener("contextmenu",this._handleContextMenu.bind(this),{passive:!1}))}decAnim(){this.animated--,this.animated<0&&(this.animated=0)}incAnim(){this.animated++}nextSurfID(){var t=0;for(let i in this.surfaces)if(this.surfaces.hasOwnProperty(i)){var e=parseInt(i);isNaN(e)||e>t&&(t=e)}return t+1}setSlabAndFog(){let t=this.camera.position.z-this.rotationGroup.position.z;t<1&&(t=1),this.camera.near=t+this.slabNear,!this.camera.ortho&&this.camera.near<1&&(this.camera.near=1),this.camera.far=t+this.slabFar,this.camera.near+1>this.camera.far&&(this.camera.far=this.camera.near+1),this.camera.fov=this.fov,this.camera.right=t*Math.tan(Math.PI/180*this.fov),this.camera.left=-this.camera.right,this.camera.top=this.camera.right/this.ASPECT,this.camera.bottom=-this.camera.top,this.camera.updateProjectionMatrix(),this.scene.fog.near=this.camera.near+this.fogStart*(this.camera.far-this.camera.near),this.scene.fog.far=this.camera.far,this.config.disableFog&&(this.scene.fog.near=this.scene.fog.far)}show(t){if(this.renderer.setViewport(),this.scene&&(this.setSlabAndFog(),this.renderer.render(this.scene,this.camera),this.viewChangeCallback&&this.viewChangeCallback(this._viewer.getView()),!t&&this.linkedViewers.length>0))for(var e=this._viewer.getView(),i=0;i0){let e=this.CAMERA_Z-this.config.lowerZoomLimit;t>e&&(t=e)}if(this.config.upperZoomLimit&&this.config.upperZoomLimit>0){let e=this.CAMERA_Z-this.config.upperZoomLimit;tthis.CAMERA_Z-1&&(t=this.CAMERA_Z-1),t}static slerp(t,e,i){if(1==i)return e.clone();if(0==i)return t.clone();let r=t.x*e.x+t.y*e.y+t.z*e.z+t.w*e.w;if(r>.9995){let r=new $.Quaternion(t.x+i*(e.x-t.x),t.y+i*(e.y-t.y),t.z+i*(e.z-t.z),t.w+i*(e.w-t.w));return r.normalize(),r}r<0&&(e=e.clone().multiplyScalar(-1),r=-r),r>1?r=1:r<-1&&(r=-1);var s=Math.acos(r)*i,n=e.clone();n.sub(t.clone().multiplyScalar(r)),n.normalize();var a=Math.cos(s),o=Math.sin(s),l=new $.Quaternion(t.x*a+n.x*o,t.y*a+n.y*o,t.z*a+n.z*o,t.w*a+n.w*o);return l.normalize(),l}constructor(t,e={}){if(this.nomouse=!1,this.glDOM=null,this.models=[],this.surfaces={},this.shapes=[],this.labels=[],this.clickables=[],this.hoverables=[],this.contextMenuEnabledObjects=[],this.current_hover=null,this.hoverDuration=500,this.longTouchDuration=1e3,this.viewer_frame=0,this.viewChangeCallback=null,this.stateChangeCallback=null,this.NEAR=1,this.FAR=800,this.CAMERA_Z=150,this.fov=20,this.linkedViewers=[],this.renderer=null,this.control_all=!1,this.scene=null,this.rotationGroup=null,this.modelGroup=null,this.fogStart=.4,this.slabNear=-50,this.slabFar=50,this.cq=new $.Quaternion(0,0,0,1),this.dq=new $.Quaternion(0,0,0,1),this.animated=0,this.animationTimers=new Set,this.isDragging=!1,this.mouseStartX=0,this.mouseStartY=0,this.touchDistanceStart=0,this.touchHold=!1,this.currentModelPos=0,this.cz=0,this.cslabNear=0,this.cslabFar=0,this.userContextMenuHandler=null,this.config=e,this.callback=this.config.callback,this.defaultcolors=this.config.defaultcolors,this.defaultcolors||(this.defaultcolors=s.elementColors.defaultColors),this.nomouse=Boolean(this.config.nomouse),this.bgColor=0,this.config.backgroundColor=this.config.backgroundColor||"#ffffff",void 0!==this.config.backgroundColor&&(this.bgColor=s.CC.color(this.config.backgroundColor).getHex()),this.config.backgroundAlpha=null==this.config.backgroundAlpha?1:this.config.backgroundAlpha,this.camerax=0,void 0!==this.config.camerax&&(this.camerax="string"==typeof this.config.camerax?parseFloat(this.config.camerax):this.config.camerax),this._viewer=this,this.container=t,null!=this.config.hoverDuration&&(this.hoverDuration=this.config.hoverDuration),void 0===this.config.antialias&&(this.config.antialias=!0),void 0===this.config.cartoonQuality&&(this.config.cartoonQuality=10),this.WIDTH=this.getWidth(),this.HEIGHT=this.getHeight(),this.setupRenderer(),this.row=null==this.config.row?0:this.config.row,this.col=null==this.config.col?0:this.config.col,this.cols=this.config.cols,this.rows=this.config.rows,this.viewers=this.config.viewers,this.control_all=this.config.control_all,this.ASPECT=this.renderer.getAspect(this.WIDTH,this.HEIGHT),this.camera=new n.Camera(this.fov,this.ASPECT,this.NEAR,this.FAR,this.config.orthographic),this.camera.position=new $.Vector3(this.camerax,0,this.CAMERA_Z),this.lookingAt=new $.Vector3,this.camera.lookAt(this.lookingAt),this.raycaster=new n.Raycaster(new $.Vector3(0,0,0),new $.Vector3(0,0,0)),this.projector=new n.Projector,this.initializeScene(),this.renderer.setClearColorHex(this.bgColor,this.config.backgroundAlpha),this.scene.fog.color=s.CC.color(this.bgColor),document.body.addEventListener("mouseup",this._handleMouseUp.bind(this)),document.body.addEventListener("touchend",this._handleMouseUp.bind(this)),this.initContainer(this.container),this.config.style&&this.setViewStyle(this.config),window.addEventListener("resize",this.resize.bind(this)),void 0!==window.ResizeObserver&&(this.divwatcher=new window.ResizeObserver(this.resize.bind(this)),this.divwatcher.observe(this.container)),void 0!==window.IntersectionObserver){let t=(t,e)=>{t.forEach((t=>{t.isIntersecting&&this.resize()}))};this.intwatcher=new window.IntersectionObserver(t),this.intwatcher.observe(this.container)}try{"function"==typeof this.callback&&this.callback(this)}catch(t){console.log("error with glviewer callback: "+t)}}targetedObjects(t,e,i){var r={x:t,y:e,z:-1};return Array.isArray(i)||(i=this.selectedAtoms(i)),0==i.length?[]:(this.raycaster.setFromCamera(r,this.camera),this.raycaster.intersectObjects(this.modelGroup,i))}modelToScreen(t){let e=!1;Array.isArray(t)||(t=[t],e=!0);let i=this.renderer.getXRatio(),r=this.renderer.getYRatio(),s=this.col,n=this.row,a=s*(this.WIDTH/i),o=(r-n-1)*(this.HEIGHT/r),l=[],h=this.canvasOffset();return t.forEach((t=>{let e=new $.Vector3(t.x,t.y,t.z);e.applyMatrix4(this.modelGroup.matrixWorld),this.projector.projectVector(e,this.camera);let s=this.WIDTH/i*(e.x+1)/2+h.left+a,n=-this.HEIGHT/r*(e.y-1)/2+h.top+o;l.push({x:s,y:n})})),e&&(l=l[0]),l}screenOffsetToModel(t,e,i){var r=t/this.WIDTH,s=e/this.HEIGHT,n=void 0===i?this.rotationGroup.position.z:i,a=this.rotationGroup.quaternion,o=new $.Vector3(0,0,n);return this.projector.projectVector(o,this.camera),o.x+=2*r,o.y-=2*s,this.projector.unprojectVector(o,this.camera),o.z=0,o.applyQuaternion(a),o}screenToModelDistance(t,e){let i=this.canvasOffset(),r=new $.Vector3(e.x,e.y,e.z);r.applyMatrix4(this.modelGroup.matrixWorld);let s=r.clone();this.projector.projectVector(r,this.camera);let n=new $.Vector3(2*(t.x-i.left)/this.WIDTH-1,2*(t.y-i.top)/-this.HEIGHT+1,r.z);return this.projector.unprojectVector(n,this.camera),n.distanceTo(s)}setViewChangeCallback(t){"function"!=typeof t&&null!=t||(this.viewChangeCallback=t)}setStateChangeCallback(t){"function"!=typeof t&&null!=t||(this.stateChangeCallback=t)}getConfig(){return this.config}setConfig(t){this.config=t,t.ambientOcclusion&&this.renderer.enableAmbientOcclusion(t.ambientOcclusion)}getInternalState(){var t={models:[],surfaces:[],shapes:[],labels:[]};for(let e=0;e{e.getCanvas().toBlob((function(e){e.arrayBuffer().then(t)}),"image/png")}))),r+=1,r==t&&(e.viewChangeCallback=s,Promise.all(n).then((t=>{let r=[];for(let e=0;e0&&(this.hoverTimeout=setTimeout((function(){a.handleHoverSelection(n.x,n.y,t)}),this.hoverDuration)),this.isDragging)){t.targetTouches&&(t.targetTouches.length>1||1===t.targetTouches.length&&!this.closeEnoughForClick(t))&&clearTimeout(this.longTouchTimeout);var l=(e-this.mouseStartX)/this.WIDTH,h=(i-this.mouseStartY)/this.HEIGHT;if(0!=this.touchDistanceStart&&t.targetTouches&&2==t.targetTouches.length)o=2,h=2*(this.calcTouchDistance(t)-this.touchDistanceStart)/(this.WIDTH+this.HEIGHT);else t.targetTouches&&3==t.targetTouches.length&&(o=1);l*=r,h*=s;var c,d=Math.hypot(l,h);if(3==o||3==this.mouseButton&&t.ctrlKey)this.slabNear=this.cslabNear+100*l,this.slabFar=this.cslabFar-100*h;else if(2==o||3==this.mouseButton||t.shiftKey)(c=.85*(this.CAMERA_Z-this.rotationGroup.position.z))<80&&(c=80),this.rotationGroup.position.z=this.cz+h*c,this.rotationGroup.position.z=this.adjustZoomToLimits(this.rotationGroup.position.z);else if(1==o||2==this.mouseButton||t.ctrlKey){var u=this.screenOffsetToModel(r*(e-this.mouseStartX),s*(i-this.mouseStartY));this.modelGroup.position.addVectors(this.currentModelPos,u)}else if((0===o||1==this.mouseButton)&&0!==d){var f=Math.sin(d*Math.PI)/d;this.dq.x=Math.cos(d*Math.PI),this.dq.y=0,this.dq.z=f*l,this.dq.w=-f*h,this.rotationGroup.quaternion.set(1,0,0,0),this.rotationGroup.quaternion.multiply(this.dq),this.rotationGroup.quaternion.multiply(this.cq)}this.show()}}_handleContextMenu(t){if(t.preventDefault(),this.closeEnoughForClick(t)){var e=this.mouseStartX,i=this.mouseStartY,r=this.canvasOffset();let n=this.mouseXY(e,i),a=n.x,o=n.y,l=this.targetedObjects(a,o,this.contextMenuEnabledObjects);var s=null;l.length&&(s=l[0].clickable);r=this.canvasOffset(),e=this.mouseStartX-r.left,i=this.mouseStartY-r.top;this.userContextMenuHandler&&(this.userContextMenuHandler(s,e,i,l,t),this.isDragging=!1)}}setContainer(t){let e=(0,L.getElement)(t)||this.container;return this.initContainer(e),this}setBackgroundColor(t,e){(void 0===e||e<0||e>1)&&(e=1);var i=s.CC.color(t);return this.scene.fog.color=i,this.bgColor=i.getHex(),this.renderer.setClearColorHex(i.getHex(),e),this.show(),this}setProjection(t){this.camera.ortho="orthographic"===t,this.setSlabAndFog()}setViewStyle(t){if((t=t||{}).style=t.style||"",t.style.includes("outline")?this.renderer.enableOutline(t):this.renderer.disableOutline(),t.style.includes("ambientOcclusion")){var e={};t.strength&&(e.strength=t.strength),t.radius&&(e.radius=t.radius),this.renderer.enableAmbientOcclusion(e)}else this.renderer.disableAmbientOcclusion();return this}updateSize(){this.renderer.setSize(this.WIDTH,this.HEIGHT),this.ASPECT=this.renderer.getAspect(this.WIDTH,this.HEIGHT),this.renderer.setSize(this.WIDTH,this.HEIGHT),this.camera.aspect=this.ASPECT,this.camera.updateProjectionMatrix()}setWidth(t){return this.WIDTH=t||this.WIDTH,this.updateSize(),this}setHeight(t){return this.HEIGHT=t||this.HEIGHT,this.updateSize(),this}resize(){this.WIDTH=this.getWidth(),this.HEIGHT=this.getHeight();let t=!1;if(this.renderer.isLost()&&this.WIDTH>0&&this.HEIGHT>0){let e=!1,i=this.container.querySelector("canvas");i&&i!=this.renderer.getCanvas()?this.config.canvas=i:(i.remove(),this.config&&null!=this.config.canvas&&(delete this.config.canvas,e=!0)),this.setupRenderer(),this.initContainer(this.container),this.renderer.setClearColorHex(this.bgColor,this.config.backgroundAlpha),t=!0,e&&(this.config.canvas=this.renderer.getCanvas())}if(0==this.WIDTH||0==this.HEIGHT?this.animated&&this._viewer.pauseAnimate():this.animated&&this._viewer.resumeAnimate(),this.updateSize(),t){let t=this.renderer.supportedExtensions();if(t.regen=!0,this.viewers)for(let e=0,i=this.viewers.length;e=0&&(this.modelGroup.remove(this.labels[i].sprite),(this.viewer_frame<0||this.labels[i].frame==this.viewer_frame)&&this.modelGroup.add(this.labels[i].sprite));for(i in this.surfaces)if(this.surfaces.hasOwnProperty(i)){var a=this.surfaces[i];for(r=0;r1||1==a[r].symmetries.length&&!a[r].symmetries[r].isIdentity()){var h,c=new n.Object3D;for(h=0;h0?this.animateMotion(e,i,this.modelGroup.position,this.adjustZoomToLimits(s),this.rotationGroup.quaternion,this.lookingAt):(this.rotationGroup.position.z=this.adjustZoomToLimits(s),this.show()),this}translate(t,e,i=0,r=!1){var s=t/this.WIDTH,n=e/this.HEIGHT,a=new $.Vector3(0,0,-this.CAMERA_Z);this.projector.projectVector(a,this.camera),a.x-=s,a.y-=n,this.projector.unprojectVector(a,this.camera),a.z=0;var o=this.lookingAt.clone().add(a);return i>0?this.animateMotion(i,r,this.modelGroup.position,this.rotationGroup.position.z,this.rotationGroup.quaternion,o):(this.lookingAt=o,this.camera.lookAt(this.lookingAt),this.show()),this}translateScene(t,e,i=0,r=!1){var s=this.screenOffsetToModel(t,e),n=this.modelGroup.position.clone().add(s);return i>0?this.animateMotion(i,r,this.modelGroup.position,this.rotationGroup.position.z,this.rotationGroup.quaternion,this.lookingAt):(this.modelGroup.position=n,this.show()),this}fitSlab(t){t=t||{};var e=this.getAtomsFromSel(t),i=(0,L.getExtent)(e),r=i[1][0]-i[0][0],s=i[1][1]-i[0][1],n=i[1][2]-i[0][2],a=Math.hypot(r,s,n);return a<5&&(a=5),this.slabNear=-a/1.9,this.slabFar=a/2,this}center(t={},e=0,i=!1){var r,s,n=this.getAtomsFromSel(t),a=(0,L.getExtent)(n);(0,L.isEmptyObject)(t)?(this.shapes.forEach((t=>{if(t&&t.boundingSphere&&t.boundingSphere.center){var e=t.boundingSphere.center,i=t.boundingSphere.radius;i>0?(n.push(new $.Vector3(e.x+i,e.y,e.z)),n.push(new $.Vector3(e.x-i,e.y,e.z)),n.push(new $.Vector3(e.x,e.y+i,e.z)),n.push(new $.Vector3(e.x,e.y-i,e.z)),n.push(new $.Vector3(e.x,e.y,e.z+i)),n.push(new $.Vector3(e.x,e.y,e.z-i))):n.push(e)}})),a=(0,L.getExtent)(n),r=n,s=a):(r=this.getAtomsFromSel({}),s=(0,L.getExtent)(r));var o=new $.Vector3(a[2][0],a[2][1],a[2][2]),l=s[1][0]-s[0][0],h=s[1][1]-s[0][1],c=s[1][2]-s[0][2],d=Math.hypot(l,h,c);d<5&&(d=5),this.slabNear=-d/1.9,this.slabFar=d/2,l=a[1][0]-a[0][0],h=a[1][1]-a[0][1],c=a[1][2]-a[0][2],(d=Math.hypot(l,h,c))<5&&(d=5);for(var u=25,f=0;fu&&(u=p)}d=2*Math.sqrt(u);var g=o.clone().multiplyScalar(-1);return e>0?this.animateMotion(e,i,g,this.rotationGroup.position.z,this.rotationGroup.quaternion,this.lookingAt):(this.modelGroup.position=g,this.show()),this}zoomTo(t={},e=0,i=!1){let r=this.getAtomsFromSel(t),s=(0,L.getExtent)(r),n=s;if((0,L.isEmptyObject)(t)){let t=r&&r.length;if(this.shapes.forEach((t=>{if(t&&t.boundingSphere)if(t.boundingSphere.box){let e=t.boundingSphere.box;r.push(new $.Vector3(e.min.x,e.min.y,e.min.z)),r.push(new $.Vector3(e.max.x,e.max.y,e.max.z))}else if(t.boundingSphere.center){var e=t.boundingSphere.center,i=t.boundingSphere.radius;i>0?(r.push(new $.Vector3(e.x+i,e.y,e.z)),r.push(new $.Vector3(e.x-i,e.y,e.z)),r.push(new $.Vector3(e.x,e.y+i,e.z)),r.push(new $.Vector3(e.x,e.y-i,e.z)),r.push(new $.Vector3(e.x,e.y,e.z+i)),r.push(new $.Vector3(e.x,e.y,e.z-i))):r.push(e)}})),n=(0,L.getExtent)(r),!t)for(let t=0;t<3;t++)s[2][t]=(n[0][t]+n[1][t])/2}else{let t=this.getAtomsFromSel({});n=(0,L.getExtent)(t)}var a=new $.Vector3(s[2][0],s[2][1],s[2][2]),o=n[1][0]-n[0][0],l=n[1][1]-n[0][1],h=n[1][2]-n[0][2],c=Math.hypot(o,l,h);c<5&&(c=5),this.slabNear=-c/1.9,this.slabFar=c/2,0===Object.keys(t).length&&(this.slabNear=Math.min(2*-c,-50),this.slabFar=Math.max(2*c,50));var d=this.config.minimumZoomToDistance||5;o=s[1][0]-s[0][0],l=s[1][1]-s[0][1],h=s[1][2]-s[0][2],(c=Math.hypot(o,l,h))u&&(u=p)}c=2*Math.sqrt(u);var g=a.clone().multiplyScalar(-1),m=-(.5*c/Math.tan(Math.PI/180*this.camera.fov/2)-this.CAMERA_Z);return m=this.adjustZoomToLimits(m),e>0?this.animateMotion(e,i,g,m,this.rotationGroup.quaternion,this.lookingAt):(this.modelGroup.position=g,this.rotationGroup.position.z=m,this.show()),this}setSlab(t,e){this.slabNear=t,this.slabFar=e}getSlab(){return{near:this.slabNear,far:this.slabFar}}addLabel(t,e={},i,r=!1){if(i){var s=(0,L.getExtent)(this.getAtomsFromSel(i));e.position={x:s[2][0],y:s[2][1],z:s[2][2]}}var n=new Label(t,e);return n.setContext(),this.modelGroup.add(n.sprite),this.labels.push(n),r||this.show(),n}addResLabels(t,e,i=!1){let r=this.labels.length;return this.applyToModels("addResLabels",t,this,e,i),this.show(),this.labels.slice(r)}addPropertyLabels(t,e,i){return this.applyToModels("addPropertyLabels",t,e,this,i),this.show(),this}removeLabel(t){for(var e=0;e0&&void 0===this.shapes[this.shapes.length-1];)this.shapes.pop();return this}removeAllShapes(){for(var t=0;t-1e-4&&s.x<1.0001&&s.y>-1e-4&&s.y<1.0001&&s.z>-1e-4&&s.z<1.0001)}}for(let s=0;ss){t.start=i,t.end=r,e.addLine(t);break}c.addVectors(i,l),t.start=i,t.end=c,e.addLine(t),i=c.clone(),d+=n,c.addVectors(i,h),i=c.clone(),d+=a}return e.finalize(),e}addCustom(t){var e=new GLShape(t=t||{});return e.shapePosition=this.shapes.length,e.addCustom(t),this.shapes.push(e),e.finalize(),e}addVolumetricData(t,e,i={}){var r=new st.VolumeData(t,e);return i.hasOwnProperty("transferfn")?this.addVolumetricRender(r,i):this.addIsosurface(r,i)}addIsosurface(t,e={},i){var r=new GLShape(e);return r.shapePosition=this.shapes.length,r.addIsosurface(t,e,i,this),this.shapes.push(r),r}addVolumetricRender(t,e){var i=new GLVolumetricRender(t,e=e||{},this);return i.shapePosition=this.shapes.length,this.shapes.push(i),i}hasVolumetricRender(){return this.renderer.supportsVolumetric()}enableFog(t){t?this.scene.fog=new n.Fog(this.bgColor,100,200):(this.config.disableFog=!0,this.show())}setFrame(t){this.viewer_frame=t;let e=this;return new Promise((function(i){var r=e.models.map((function(i){return i.setFrame(t,e)}));Promise.all(r).then((function(){i()}))}))}getFrame(){return this.viewer_frame}getNumFrames(){var t=0;for(let e=0;et&&(t=this.models[e].getNumFrames());for(let e=0;e=t&&(t=this.shapes[e].frame+1);for(let e=0;e=t&&(t=this.labels[e].frame+1);return t}animate(t){this.incAnim();var e=100,i="forward",r=1/0;(t=t||{}).interval&&(e=t.interval),t.loop&&(i=t.loop),t.reps&&(r=t.reps);var s=this.getNumFrames(),n=this,a=0;t.startFrame&&(a=t.startFrame%s);var o=1;t.step&&(r/=o=t.step);var l,h,c=0,d=s*r,u=new Date,f=function(t){u=new Date,"forward"==t?n.setFrame(a).then((function(){a=(a+o)%s,l()})):"backward"==t?n.setFrame(s-1-a).then((function(){a=(a+o)%s,l()})):n.setFrame(a).then((function(){o*=(a+=o)%(s-1)==0?-1:1,l()}))};return l=function(){if(n.render(),n.getCanvas().isConnected)if(++c>=d||!n.isAnimated())h.cancel(),n.animationTimers.delete(h),n.decAnim();else{var t=e-((new Date).getTime()-u.getTime());t=t>0?t:0,n.animationTimers.delete(h),h=new L.PausableTimer(f,t,i),n.animationTimers.add(h)}else n.stopAnimate()},h=new L.PausableTimer(f,0,i),this.animationTimers.add(h),this}stopAnimate(){return this.animated=0,this.animationTimers.forEach((function(t){t.cancel()})),this.animationTimers=new Set,this}pauseAnimate(){return this.animationTimers.forEach((function(t){t.pause()})),this}resumeAnimate(){return this.animationTimers.forEach((function(t){t.resume()})),this}isAnimated(){return this.animated>0}getModelOpt(t){return t&&!t.defaultcolors?(t.defaultcolors=this.defaultcolors,t.cartoonQuality=t.cartoonQuality||this.config.cartoonQuality):void 0===t&&(t={defaultcolors:this.defaultcolors,cartoonQuality:this.config.cartoonQuality}),t}addModel(t,e="",i){i=this.getModelOpt(i);var r=new GLModel(this.models.length,i);return r.addMolData(t,e,i),this.models.push(r),r}addModels(t,e,i){(i=this.getModelOpt(i)).multimodel=!0,i.frames=!0;for(var r=GLModel.parseMolData(t,e,i),s=0;s0&&void 0===this.models[this.models.length-1];)this.models.pop();return this}}removeAllModels(){for(var t=0;te[1][0]||r.ye[1][1]||r.ze[1][2]||i.push(r))}return i}static volume(t){return(t[1][0]-t[0][0])*(t[1][1]-t[0][1])*(t[1][2]-t[0][2])}carveUpExtent(t,e,i){let r=[],s={};for(let t=0,i=e.length;tr&&i>s?0:r>i&&r>s?1:2;var n=a(t),l=a(t),h=(t[1][e]-t[0][e])/2+t[0][e];n[1][e]=h,l[0][e]=h;var c=o(n),d=o(l);return c.concat(d)},l=o(t);for(let t=0,s=l.length;t0)for(let e=0,i=c.length;e1||1==v.length&&!v[0].isIdentity()){m=!0;break}}var _=function(t,i,s){var n;f=a?GLViewer.shallowCopy(c.getAtomsFromSel(a)):s;var o=(0,L.getExtent)(s,!0);if(e.map&&e.map.prop){var u=e.map.prop;let t=(0,r.getGradient)(e.map.scheme||e.map.gradient||new r.Gradient.RWB),i=t.range();i||(i=(0,L.getPropertyRange)(s,u)),e.colorscheme={prop:u,gradient:t}}for(let t=0,r=i.length;t0){var m=(0,L.getExtent)(f,!0);g.sort((function(t,e){var i=function(t,e){var i=t.extent,r=i[1][0]-i[0][0],s=i[1][1]-i[0][1],n=i[1][2]-i[0][2],a=r-e[2][0];a*=a;var o=s-e[2][1];o*=o;var l=n-e[2][2];return a+o+(l*=l)};return i(t,m)-i(e,m)}))}var v=[];for(let t=0,e=i.length;t0&&(y.push({geo:new n.Geometry(!0),mat:h,done:!1,finished:!1,symmetries:this.models[g].getSymmetries()}),A.push(_(y[y.length-1],x[g],w[g])));b=Promise.all(A)}else y.push({geo:new n.Geometry(!0),mat:h,done:!1,finished:!1,symmetries:[new $.Matrix4]}),b=_(y[y.length-1],u,p);return this.surfaces[l]=y,b.surfid=l,o&&"function"==typeof o?(b.then((function(t){o(t)})),l):b}setSurfaceMaterialStyle(t,e){if((0,L.adjustVolumeStyle)(e),this.surfaces[t]){var i=this.surfaces[t];for(let t=0;t0?this.camera.position.x=i*Math.tan(Math.PI/180*e):this.camera.position.x=-i*Math.tan(Math.PI/180*e),this.camera.lookAt(new $.Vector3(0,0,this.rotationGroup.position.z)),this.camera.position.x}setDefaultCartoonQuality(t){this.config.cartoonQuality=t}}function qt(t,e){if(t=(0,L.getElement)(t)){e=e||{};try{return new GLViewer(t,e)}catch(t){throw"error creating viewer: "+t}}}function Yt(t,e={},i={}){if(t=(0,L.getElement)(t)){var r=[],s=document.createElement("canvas");i.rows=e.rows,i.cols=e.cols,i.control_all=null!=e.control_all&&e.control_all,t.appendChild(s);try{for(var n=0;n{var l=[],h=[],c="";"static"==o.style.position&&(o.style.position="relative");var d=null;if(n=null,o.dataset.pdb)l.push("https://files.rcsb.org/view/"+o.dataset.pdb+".pdb"),h.push("pdb");else if(o.dataset.cid)h.push("sdf"),l.push("https://pubchem.ncbi.nlm.nih.gov/rest/pug/compound/cid/"+o.dataset.cid+"/SDF?record_type=3d");else if(o.dataset.href||o.dataset.url){if(c=o.dataset.href?o.dataset.href:o.dataset.url,l.push(c),"gz"==(n=c.substring(c.lastIndexOf(".")+1))){let t=c.substring(0,c.lastIndexOf(".")).lastIndexOf(".");n=c.substring(t+1)}h.push(n);var u=c.substring(c.lastIndexOf("/")+1,c.lastIndexOf("."));"/"==u&&(u=c.substring(c.lastIndexOf("/")+1)),o.dataset[h[h.length-1]]=u}let f=o.dataset;for(i in f)"pdb"===i.substring(0,3)&&"pdb"!==i?(l.push("https://files.rcsb.org/view/"+f[i]+".pdb"),h.push("pdb")):"href"===i.substring(0,4)&&"href"!==i?(c=f[i],l.push(c),h.push(c.substring(c.lastIndexOf(".")+1))):"cid"===i.substring(0,3)&&"cid"!==i&&(l.push("https://pubchem.ncbi.nlm.nih.gov/rest/pug/compound/cid/"+f[i]+"/SDF?record_type=3d"),h.push("sdf"));var p={};o.dataset.options&&(p=(0,L.specStringToObject)(o.dataset.options));var g=s.CC.color(o.dataset.backgroundcolor),m=o.dataset.backgroundalpha;m=null==m?1:parseFloat(m);var v={line:{}};o.dataset.style&&(v=(0,L.specStringToObject)(o.dataset.style));var _={};o.dataset.select&&(_=(0,L.specStringToObject)(o.dataset.select));var y=[],b=[],x=[],w={},A=null,C=o.dataset,S=/style(.+)/,M=/surface(.*)/,z=/labelres(.*)/,T=[];for(r in C)Object.prototype.hasOwnProperty.call(C,r)&&T.push(r);for(T.sort(),i=0;i{d.loadSurface("VDW",e,r,t)})):t.addSurface(Q.VDW,r,e,e)}for(i=0;ifunction(n){c=l[t];var a=r.dataset.type||r.dataset.datatype||h[t];if(s.addModel(n,a,p),d){var o=r.dataset[h[t]];d.setModelTitle(o)}if(t+=1,t{"complete"===document.readyState&&$t()},window&&(window.$3Dmol=e)},865:(t,e,i)=>{"use strict";i.r(e),i.d(e,{CUBE:()=>a});var r=i(638),s=i(392);const n={1:"H",2:"He",3:"Li",4:"Be",5:"B",6:"C",7:"N",8:"O",9:"F",10:"Ne",11:"Na",12:"Mg",13:"Al",14:"Si",15:"P",16:"S",17:"Cl",18:"Ar",19:"K",20:"Ca",21:"Sc",22:"Ti",23:"V",24:"Cr",25:"Mn",26:"Fe",27:"Co",28:"Ni",29:"Cu",30:"Zn",31:"Ga",32:"Ge",33:"As",34:"Se",35:"Br",36:"Kr",37:"Rb",38:"Sr",39:"Y",40:"Zr",41:"Nb",42:"Mo",43:"Tc",44:"Ru",45:"Rh",46:"Pd",47:"Ag",48:"Cd",49:"In",50:"Sn",51:"Sb",52:"Te",53:"I",54:"Xe",55:"Cs",56:"Ba",71:"Lu",72:"Hf",73:"Ta",74:"W",75:"Re",76:"Os",77:"Ir",78:"Pt",79:"Au",80:"Hg",81:"Tl",82:"Pb",83:"Bi",84:"Po",85:"At",86:"Rn",87:"Fr",88:"Ra",104:"Rf",105:"Db",106:"Sg",107:"Bh",108:"Hs",109:"Mt",110:"Ds",111:"Rg",112:"Cn",113:"Nh",114:"Fl",115:"Mc",116:"Lv",117:"Ts",118:"Og",57:"La",58:"Ce",59:"Pr",60:"Nd",61:"Pm",62:"Sm",63:"Eu",64:"Gd",65:"Tb",66:"Dy",67:"Ho",68:"Er",69:"Tm",70:"Yb",89:"Ac",90:"Th",91:"Pa",92:"U",93:"Np",94:"Pu",95:"Am",96:"Cm",97:"Bk",98:"Cf",99:"Es",100:"Fm",101:"Md",102:"No"};function a(t,e){e=e||{};const i=[[]];let a=t.split(/\r?\n/);const o=void 0===e.assignBonds||e.assignBonds;if(a.length<6)return i;let l=a[2].replace(/^\s+/,"").replace(/\s+/g," ").split(" ");const h=Math.abs(parseFloat(l[0]));let c={origin:void 0,size:void 0,unit:void 0,matrix4:void 0,matrix:void 0};const d=c.origin=new r.Vector3(parseFloat(l[1]),parseFloat(l[2]),parseFloat(l[3]));l=a[3].replace(/^\s+/,"").replace(/\s+/g," ").split(" "),l=a[3].replace(/^\s+/,"").replace(/\s+/g," ").split(" ");const u=l[0]>0?.529177:1;d.multiplyScalar(u);const f=Math.abs(l[0]),p=new r.Vector3(parseFloat(l[1]),parseFloat(l[2]),parseFloat(l[3])).multiplyScalar(u);l=a[4].replace(/^\s+/,"").replace(/\s+/g," ").split(" ");const g=Math.abs(l[0]),m=new r.Vector3(parseFloat(l[1]),parseFloat(l[2]),parseFloat(l[3])).multiplyScalar(u);l=a[5].replace(/^\s+/,"").replace(/\s+/g," ").split(" ");const v=Math.abs(l[0]),_=new r.Vector3(parseFloat(l[1]),parseFloat(l[2]),parseFloat(l[3])).multiplyScalar(u);if(c.size={x:f,y:g,z:v},c.unit=new r.Vector3(p.x,m.y,_.z),0!=p.y||0!=p.z||0!=m.x||0!=m.z||0!=_.x||0!=_.y){c.matrix4=new r.Matrix4(p.x,m.x,_.x,0,p.y,m.y,_.y,0,p.z,m.z,_.z,0,0,0,0,1);let t=(new r.Matrix4).makeTranslation(d.x,d.y,d.z);c.matrix4=c.matrix4.multiplyMatrices(t,c.matrix4),c.matrix=c.matrix4.matrix3FromTopLeft(),c.origin=new r.Vector3(0,0,0),c.unit=new r.Vector3(1,1,1)}i.modelData=[{cryst:c}],a=a.splice(6,h);for(var y=i[i.length-1].length,b=y+a.length,x=y;x{"use strict";i.r(e),i.d(e,{VASP:()=>n});var r=i(638),s=i(392);function n(t,e={}){var i=[[]],n={};const a=void 0===e.assignBonds||e.assignBonds;var o=t.replace(/^\s+/,"").split(/\r?\n/);if(o.length<3)return i;if(!o[1].match(/\d+/))return console.log("Warning: second line of the vasp structure file must be a number"),i;if(n.length=parseFloat(o[1]),n.length<0)return console.log("Warning: Vasp implementation for negative lattice lengths is not yet available"),i;n.xVec=new Float32Array(o[2].replace(/^\s+/,"").split(/\s+/)),n.yVec=new Float32Array(o[3].replace(/^\s+/,"").split(/\s+/)),n.zVec=new Float32Array(o[4].replace(/^\s+/,"").split(/\s+/));var l=new r.Matrix3(n.xVec[0],n.xVec[1],n.xVec[2],n.yVec[0],n.yVec[1],n.yVec[2],n.zVec[0],n.zVec[1],n.zVec[2]);l.multiplyScalar(n.length),i.modelData=[{symmetries:[],cryst:{matrix:l}}];var h=o[5].trim().split(/\s+/),c=new Int16Array(o[6].trim().split(/\s+/)),d=o[7].trim(),u=!1;if(d.match(/S/)&&(u=!0,d=o[8].trim()),"c"==d.toLowerCase()[0])d="cartesian";else{if("d"!=d.toLowerCase()[0])return console.log("Warning: Unknown vasp mode in POSCAR file: mode must be either C(artesian) or D(irect)"),i;d="direct"}if(h.length!=c.length)return console.log("Warning: declaration of atomary species wrong:"),console.log(h),console.log(c),i;u?o.splice(0,9):o.splice(0,8);for(var f=0,p=0,g=h.length;p{"use strict";i.r(e),i.d(e,{areConnected:()=>n});var r=i(40);const s=new Set(["Na","K","Ca","Mg","Mn","Sr"]);function n(t,e,i){if(i&&i.unboundCations&&(s.has(t.elem)||s.has(e.elem)))return!1;let n=(0,r.bondLength)(t.elem)+(0,r.bondLength)(e.elem);n+=.25,n*=n;let a=t.x-e.x;if(a*=a,a>n)return!1;let o=t.y-e.y;if(o*=o,o>n)return!1;let l=t.z-e.z;if(l*=l,l>n)return!1;const h=a+o+l;return!(isNaN(h)||h<.5||h>n||t.altLoc!==e.altLoc&&""!==t.altLoc.trim()&&""!==e.altLoc.trim())}},392:(t,e,i)=>{"use strict";i.r(e),i.d(e,{assignBonds:()=>a});var r=i(408);const s=[{x:0,y:0,z:1},{x:0,y:1,z:-1},{x:0,y:1,z:0},{x:0,y:1,z:1},{x:1,y:-1,z:-1},{x:1,y:-1,z:0},{x:1,y:-1,z:1},{x:1,y:0,z:-1},{x:1,y:0,z:0},{x:1,y:0,z:1},{x:1,y:1,z:-1},{x:1,y:1,z:0},{x:1,y:1,z:1}],n=4.95;function a(t,e){for(let e=0,i=t.length;e{"use strict";i.r(e),i.d(e,{bondLength:()=>s,bondTable:()=>r,setBondLength:()=>n});let r={H:.37,He:.32,Li:1.34,Be:.9,B:.82,C:.77,N:.75,O:.73,F:.71,Ne:.69,Na:1.54,Mg:1.3,Al:1.18,Si:1.11,P:1.06,S:1.02,Cl:.99,Ar:.97,K:1.96,Ca:1.74,Sc:1.44,Ti:1.56,V:1.25,Mn:1.39,Fe:1.25,Co:1.26,Ni:1.21,Cu:1.38,Zn:1.31,Ga:1.26,Ge:1.22,Se:1.16,Br:1.14,Kr:1.1,Rb:2.11,Sr:1.92,Y:1.62,Zr:1.48,Nb:1.37,Mo:1.45,Tc:1.56,Ru:1.26,Rh:1.35,Pd:1.31,Ag:1.53,Cd:1.48,In:1.44,Sn:1.41,Sb:1.38,Te:1.35,I:1.33,Xe:1.3,Cs:2.25,Ba:1.98,Lu:1.6,Hf:1.5,Ta:1.38,W:1.46,Re:1.59,Os:1.44,Ir:1.37,Pt:1.28,Au:1.44,Hg:1.49,Tl:1.48,Pb:1.47,Bi:1.46,Rn:1.45};function s(t){return r[t]||1.6}function n(t,e){e<0&&(e=0),r[t]=e}},864:(__unused_webpack_module,__webpack_exports__,__webpack_require__)=>{"use strict";__webpack_require__.r(__webpack_exports__),__webpack_require__.d(__webpack_exports__,{PausableTimer:()=>PausableTimer,adjustVolumeStyle:()=>adjustVolumeStyle,base64ToArray:()=>base64ToArray,deepCopy:()=>deepCopy,download:()=>download,extend:()=>extend,get:()=>get,getAtomProperty:()=>getAtomProperty,getColorFromStyle:()=>getColorFromStyle,getElement:()=>getElement,getExtent:()=>getExtent,getPropertyRange:()=>getPropertyRange,getbin:()=>getbin,inflateString:()=>inflateString,isEmptyObject:()=>isEmptyObject,isNumeric:()=>isNumeric,makeFunction:()=>makeFunction,mergeGeos:()=>mergeGeos,specStringToObject:()=>specStringToObject});var _Gradient__WEBPACK_IMPORTED_MODULE_0__=__webpack_require__(546),_VolumeData__WEBPACK_IMPORTED_MODULE_1__=__webpack_require__(848),_colors__WEBPACK_IMPORTED_MODULE_2__=__webpack_require__(222),pako__WEBPACK_IMPORTED_MODULE_3__=__webpack_require__(75);function extend(t,e){for(var i in e)e.hasOwnProperty(i)&&void 0!==e[i]&&(t[i]=e[i]);return t}function deepCopy(t){let e,i,r;if(null==t)return{};if("object"!=typeof t||null===t)return t;for(r in e=Array.isArray(t)?[]:{},t)i=t[r],e[r]=deepCopy(i);return e}function isNumeric(t){var e=typeof t;return("number"===e||"string"===e)&&!isNaN(t-parseFloat(t))}function isEmptyObject(t){var e;for(e in t)return!1;return!0}function makeFunction(callback){return callback&&"string"==typeof callback&&(callback=eval("("+callback+")")),callback&&"function"!=typeof callback?(console.warn("Invalid callback provided."),()=>{}):callback}function adjustVolumeStyle(t){t&&(!t.volformat||t.voldata instanceof _VolumeData__WEBPACK_IMPORTED_MODULE_1__.VolumeData||(t.voldata=new _VolumeData__WEBPACK_IMPORTED_MODULE_1__.VolumeData(t.voldata,t.volformat)),t.volscheme&&(t.volscheme=_Gradient__WEBPACK_IMPORTED_MODULE_0__.Gradient.getGradient(t.volscheme)))}function getExtent(t,e){var i,r,s,n,a,o,l,h,c,d,u=!e;if(i=r=s=9999,n=a=o=-9999,l=h=c=d=0,0===t.length)return[[0,0,0],[0,0,0],[0,0,0]];for(var f=0;fp.x?n:p.x,a=a>p.y?a:p.y,o=o>p.z?o:p.z,p.symmetries&&u))for(var g=0;gp.symmetries[g].x?n:p.symmetries[g].x,a=a>p.symmetries[g].y?a:p.symmetries[g].y,o=o>p.symmetries[g].z?o:p.symmetries[g].z}return[[i,r,s],[n,a,o],[l/d,h/d,c/d]]}function getPropertyRange(t,e){for(var i=Number.POSITIVE_INFINITY,r=Number.NEGATIVE_INFINITY,s=0,n=t.length;sr&&(r=a))}return isFinite(i)||isFinite(r)?isFinite(i)?isFinite(r)||(r=i):i=r:i=r=0,[i,r]}class PausableTimer{constructor(t,e,i){this.total_time_run=0,this.fn=t,this.arg=i,this.countdown=e,this.start_time=(new Date).getTime(),this.ident=setTimeout(t,e,i)}cancel(){clearTimeout(this.ident)}pause(){clearTimeout(this.ident),this.total_time_run=(new Date).getTime()-this.start_time}resume(){this.ident=setTimeout(this.fn,Math.max(0,this.countdown-this.total_time_run),this.arg)}}function base64ToArray(t){for(var e=window.atob(t),i=e.length,r=new Uint8Array(i),s=0;s=0?parseFloat(t):parseInt(t):"true"===t||"false"!==t&&t},i={};if("all"===(t=t.replace(/%7E/g,"~")))return i;for(var r=t.split(";"),s=0;st.text()));return e?i.then(e):i}function getbin(t,e,i,r){var s;return s="POST"==i?fetch(t,{method:"POST",body:r}).then((t=>checkStatus(t))).then((t=>t.arrayBuffer())):fetch(t).then((t=>checkStatus(t))).then((t=>t.arrayBuffer())),e?s.then(e):s}function download(t,e,i,r){var s="",n="",a="",o=null,l=e.addModel();if(t.indexOf(":")<0&&(t=4==t.length?"pdb:"+t:isNaN(t)?"url:"+t:"cid:"+t),"mmtf:"==t.substring(0,5)&&(console.warn("WARNING: MMTF now deprecated. Reverting to bcif."),t="bcif:"+t.slice(5)),"bcif:"===t.substring(0,5))t=t.substring(5).toUpperCase(),a="https://models.rcsb.org/"+t+".bcif.gz",i&&void 0===i.noComputeSecondaryStructure&&(i.noComputeSecondaryStructure=!0),o=new Promise((function(t){getbin(a).then((function(r){l.addMolData(r,"bcif.gz",i),e.zoomTo(),e.render(),t(l)}),(function(){console.error("fetch of "+a+" failed.")}))}));else{if("pdb:"===t.substring(0,4)){if(s="bcif",i&&i.format&&(s=i.format),i&&void 0===i.noComputeSecondaryStructure&&(i.noComputeSecondaryStructure=!0),!(t=t.substring(4).toUpperCase()).match(/^[1-9][A-Za-z0-9]{3}$/))return void alert("Wrong PDB ID");"bcif"==s?a="https://models.rcsb.org/"+t.toUpperCase()+".bcif.gz":(n=i&&i.pdbUri?i.pdbUri:"https://files.rcsb.org/view/",a=n+t+"."+s)}else if("cid:"==t.substring(0,4)){if(s="sdf",!(t=t.substring(4)).match(/^[0-9]+$/))return void alert("Wrong Compound ID");a="https://pubchem.ncbi.nlm.nih.gov/rest/pug/compound/cid/"+t+"/SDF?record_type=3d"}else"url:"==t.substring(0,4)&&(a=t.substring(4),s=a);var h=function(t){l.addMolData(t,s,i),e.zoomTo(),e.render()};o=new Promise((function(e){"bcif"==s?getbin(a).then((function(t){h(t),e(l)})).catch((function(){n=i&&i.pdbUri?i.pdbUri:"https://files.rcsb.org/view/",a=n+t+".pdb",s="pdb",console.warn("falling back to pdb format"),get(a).then((function(t){h(t),e(l)})).catch((function(t){h(""),e(l),console.error("fetch of "+a+" failed: "+t.statusText)}))})):get(a).then((function(t){h(t),e(l)})).catch((function(t){h(""),e(l),console.error("fetch of "+a+" failed: "+t.statusText)}))}))}return r?(o.then((function(t){r(t)})),l):o}function getColorFromStyle(t,e){let i=e.colorscheme;if(void 0!==_colors__WEBPACK_IMPORTED_MODULE_2__.builtinColorSchemes[i])i=_colors__WEBPACK_IMPORTED_MODULE_2__.builtinColorSchemes[i];else if("string"==typeof i&&i.endsWith("Carbon")){let t=i.substring(0,i.lastIndexOf("Carbon")).toLowerCase();if(void 0!==_colors__WEBPACK_IMPORTED_MODULE_2__.htmlColors[t]){let e=Object.assign({},_colors__WEBPACK_IMPORTED_MODULE_2__.elementColors.defaultColors);e.C=_colors__WEBPACK_IMPORTED_MODULE_2__.htmlColors[t],_colors__WEBPACK_IMPORTED_MODULE_2__.builtinColorSchemes[i]={prop:"elem",map:e},i=_colors__WEBPACK_IMPORTED_MODULE_2__.builtinColorSchemes[i]}}let r=t.color;if(void 0!==e.color&&"spectrum"!=e.color&&(r=e.color),void 0!==i){let n,a;if(void 0!==_colors__WEBPACK_IMPORTED_MODULE_2__.elementColors[i])i=_colors__WEBPACK_IMPORTED_MODULE_2__.elementColors[i],void 0!==i[t[i.prop]]&&(r=i.map[t[i.prop]]);else if(void 0!==i[t[i.prop]])r=i.map[t[i.prop]];else if(void 0!==i.prop&&void 0!==i.gradient){n=i.prop;var s=i.gradient;s instanceof _Gradient__WEBPACK_IMPORTED_MODULE_0__.GradientType||(s=(0,_Gradient__WEBPACK_IMPORTED_MODULE_0__.getGradient)(i));let e=s.range()||[-1,1];a=getAtomProperty(t,n),null!=a&&(r=s.valueToHex(a,e))}else void 0!==i.prop&&void 0!==i.map?(n=i.prop,a=getAtomProperty(t,n),void 0!==i.map[a]&&(r=i.map[a])):void 0!==e.colorscheme[t.elem]?r=e.colorscheme[t.elem]:console.warn("Could not interpret colorscheme "+i)}else void 0!==e.colorfunc&&(r=e.colorfunc(t));return _colors__WEBPACK_IMPORTED_MODULE_2__.CC.color(r)}function getElement(t){let e=t;return"string"==typeof t?e=document.querySelector("#"+t):"object"==typeof t&&t.get&&(e=t.get(0)),e}function inflateString(t,e=!0){let i;if("string"==typeof t){i=(new TextEncoder).encode(t)}else i=new Uint8Array(t);return(0,pako__WEBPACK_IMPORTED_MODULE_3__.inflate)(i,{to:e?"string":null})}},111:(t,e,i)=>{var r;r={},t.exports=r,function(t,e){t.toRGBA8=function(e){var i=e.width,r=e.height;if(null==e.tabs.acTL)return[t.toRGBA8.decodeImage(e.data,i,r,e).buffer];var s=[];null==e.frames[0].data&&(e.frames[0].data=e.data);for(var n,a=new Uint8Array(i*r*4),o=0;o>3)]>>7-(7&p)&1);l[M]=b[z],l[M+1]=b[z+1],l[M+2]=b[z+2],l[M+3]=T>2)]>>6-((3&p)<<1)&3),l[M]=b[z],l[M+1]=b[z+1],l[M+2]=b[z+2],l[M+3]=T>1)]>>4-((1&p)<<2)&15),l[M]=b[z],l[M+1]=b[z+1],l[M+2]=b[z+2],l[M+3]=T>3]>>7-(7&p)&1))==255*m?0:255;h[p]=F<<24|E<<16|E<<8|E}if(2==d)for(p=0;p>2]>>6-((3&p)<<1)&3))==85*m?0:255,h[p]=F<<24|E<<16|E<<8|E;if(4==d)for(p=0;p>1]>>4-((1&p)<<2)&15))==17*m?0:255,h[p]=F<<24|E<<16|E<<8|E;if(8==d)for(p=0;p>3,o=Math.ceil(r*n/8),l=new Uint8Array(s*o),h=0,c=[0,0,4,0,2,0,1],d=[0,4,0,2,0,1,0],u=[8,8,8,4,4,2,2],f=[8,8,4,4,2,2,1],p=0;p<7;){for(var g=u[p],m=f[p],v=0,_=0,y=c[p];y>3])>>7-(7&S)&1,l[A*o+(C>>3)]|=M<<7-(3&C)),2==n&&(M=(M=e[S>>3])>>6-(7&S)&3,l[A*o+(C>>2)]|=M<<6-((3&C)<<1)),4==n&&(M=(M=e[S>>3])>>4-(7&S)&15,l[A*o+(C>>1)]|=M<<4-((1&C)<<2)),n>=8)for(var z=A*o+C*a,T=0;T>3)+T];S+=n,C+=m}w++,A+=g}v*_!=0&&(h+=_*(1+x)),p+=1}return l},t.decode._getBPP=function(t){return[1,null,3,1,2,null,4][t.ctype]*t.depth},t.decode._filterZero=function(e,i,r,s,n){var a=t.decode._getBPP(i),o=Math.ceil(s*a/8),l=t.decode._paeth;a=Math.ceil(a/8);for(var h=0;h>1)&255;if(4==u)for(f=a;f>1)&255;for(f=a;f>1)&255}if(4==u){for(f=0;f>8&255,t[e+1]=255&i},readUint:function(t,e){return 16777216*t[e]+(t[e+1]<<16|t[e+2]<<8|t[e+3])},writeUint:function(t,e,i){t[e]=i>>24&255,t[e+1]=i>>16&255,t[e+2]=i>>8&255,t[e+3]=255&i},readASCII:function(t,e,i){for(var r="",s=0;s=0&&o>=0?(d=f*e+p<<2,u=(o+f)*s+a+p<<2):(d=(-o+f)*e-a+p<<2,u=f*s+p<<2),0==l)r[u]=t[d],r[u+1]=t[d+1],r[u+2]=t[d+2],r[u+3]=t[d+3];else if(1==l){var g=t[d+3]*(1/255),m=t[d]*g,v=t[d+1]*g,_=t[d+2]*g,y=r[u+3]*(1/255),b=r[u]*y,x=r[u+1]*y,w=r[u+2]*y,A=1-g,C=g+y*A,S=0==C?0:1/C;r[u+3]=255*C,r[u+0]=(m+b*A)*S,r[u+1]=(v+x*A)*S,r[u+2]=(_+w*A)*S}else if(2==l)g=t[d+3],m=t[d],v=t[d+1],_=t[d+2],y=r[u+3],b=r[u],x=r[u+1],w=r[u+2],g==y&&m==b&&v==x&&_==w?(r[u]=0,r[u+1]=0,r[u+2]=0,r[u+3]=0):(r[u]=m,r[u+1]=v,r[u+2]=_,r[u+3]=g);else if(3==l){if(g=t[d+3],m=t[d],v=t[d+1],_=t[d+2],y=r[u+3],b=r[u],x=r[u+1],w=r[u+2],g==y&&m==b&&v==x&&_==w)continue;if(g<220&&y>20)return!1}return!0},t.encode=function(e,i,r,s,n,a){null==s&&(s=0),null==a&&(a=!1);for(var o=new Uint8Array(e[0].byteLength*e.length+100),l=[137,80,78,71,13,10,26,10],h=0;h<8;h++)o[h]=l[h];var c=8,d=t._bin,u=t.crc.crc,f=d.writeUint,p=d.writeUshort,g=d.writeASCII,m=t.encode.compressPNG(e,i,r,s,a);f(o,c,13),g(o,c+=4,"IHDR"),f(o,c+=4,i),f(o,c+=4,r),o[c+=4]=m.depth,o[++c]=m.ctype,o[++c]=0,o[++c]=0,o[++c]=0,f(o,++c,u(o,c-17,17)),f(o,c+=4,1),g(o,c+=4,"sRGB"),o[c+=4]=1,f(o,++c,u(o,c-5,5)),c+=4;var v=e.length>1;if(v&&(f(o,c,8),g(o,c+=4,"acTL"),f(o,c+=4,e.length),f(o,c+=4,0),f(o,c+=4,u(o,c-12,12)),c+=4),3==m.ctype){for(f(o,c,3*(M=m.plte.length)),g(o,c+=4,"PLTE"),c+=4,h=0;h>8&255,w=y>>16&255;o[c+_+0]=b,o[c+_+1]=x,o[c+_+2]=w}if(f(o,c+=3*M,u(o,c-3*M-4,3*M+4)),c+=4,m.gotAlpha){for(f(o,c,M),g(o,c+=4,"tRNS"),c+=4,h=0;h>24&255;f(o,c+=M,u(o,c-M-4,M+4)),c+=4}}for(var A=0,C=0;C=300))break}}var x=!!g&&n,w=v.length;w<=256&&0==a&&(l=w<=2?1:w<=4?2:w<=16?4:8,n&&(l=8),g=!0);var A=[];for(d=0;dB&&(B=V),GN&&(N=G));var j=-1==B?1:(B-P+1)*(N-U+1);j>1)]|=m[S[q+V]]<<4-4*(1&V);else if(2==l)for(V=0;V>2)]|=m[S[q+V]]<<6-2*(3&V);else if(1==l)for(V=0;V>3)]|=m[S[q+V]]<<7-1*(7&V)}C=H,o=3,h=1}else if(0==g&&1==e.length){H=new Uint8Array(T*E*3);var Y=T*E;for(p=0;p5e5)||2!=l&&3!=l&&4!=l){for(var h=0;h>1)+256&255;if(4==a)for(c=n;c>1)&255;for(c=n;c>1)&255}if(4==a){for(c=0;c>>1:i>>>=1;t[e]=i}return t}(),update:function(e,i,r,s){for(var n=0;n>>8;return e},crc:function(e,i,r){return 4294967295^t.crc.update(4294967295,e,i,r)}},t.quantize=function(e,i,r){for(var s=[],n=0,a=0;ag&&(g=p[a].est.L,m=a);if(g<.001)break;var v=p[m],_=t.quantize.splitPixels(o,l,v.i0,v.i1,v.est.e,v.est.eMq255),y={i0:v.i0,i1:_,bst:null,est:null,tdst:0,left:null,right:null};y.bst=t.quantize.stats(o,y.i0,y.i1),y.est=t.quantize.estats(y.bst);var b={i0:_,i1:v.i1,bst:null,est:null,tdst:0,left:null,right:null};for(b.bst={R:[],m:[],N:v.bst.N-y.bst.N},a=0;a<16;a++)b.bst.R[a]=v.bst.R[a]-y.bst.R[a];for(a=0;a<4;a++)b.bst.m[a]=v.bst.m[a]-y.bst.m[a];b.est=t.quantize.estats(b.bst),v.left=y,v.right=b,p[m]=y,p.push(b)}p.sort((function(t,e){return e.bst.N-t.bst.N}));for(var x=0;x>2]=L.est.rgba}s[x]=C.buffer}return{bufs:s,plte:p}},t.quantize.getNearest=function(e,i,r,s,n){if(null==e.left)return e.tdst=t.quantize.dist(e.est.q,i,r,s,n),e;var a=t.quantize.planeDst(e.est,i,r,s,n),o=e.left,l=e.right;a>0&&(o=e.right,l=e.left);var h=t.quantize.getNearest(o,i,r,s,n);if(h.tdst<=a*a)return h;var c=t.quantize.getNearest(l,i,r,s,n);return c.tdsta;)s-=4;if(r>=s)break;var l=i[r>>2];i[r>>2]=i[s>>2],i[s>>2]=l,r+=4,s-=4}for(;o(e,r,n)>a;)r-=4;return r+4},t.quantize.vecDot=function(t,e,i){return t[e]*i[0]+t[e+1]*i[1]+t[e+2]*i[2]+t[e+3]*i[3]},t.quantize.stats=function(t,e,i){for(var r=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],s=[0,0,0,0],n=i-e>>2,a=e;a>>0}},t.M4={multVec:function(t,e){return[t[0]*e[0]+t[1]*e[1]+t[2]*e[2]+t[3]*e[3],t[4]*e[0]+t[5]*e[1]+t[6]*e[2]+t[7]*e[3],t[8]*e[0]+t[9]*e[1]+t[10]*e[2]+t[11]*e[3],t[12]*e[0]+t[13]*e[1]+t[14]*e[2]+t[15]*e[3]]},dot:function(t,e){return t[0]*e[0]+t[1]*e[1]+t[2]*e[2]+t[3]*e[3]},sml:function(t,e){return[t*e[0],t*e[1],t*e[2],t*e[3]]}},t.encode.alphaMul=function(t,e){for(var i=new Uint8Array(t.length),r=t.length>>2,s=0;s{"use strict";var r={};(0,i(981).assign)(r,i(71),i(3),i(681)),t.exports=r},71:(t,e,i)=>{"use strict";var r=i(107),s=i(981),n=i(972),a=i(834),o=i(746),l=Object.prototype.toString;function h(t){if(!(this instanceof h))return new h(t);this.options=s.assign({level:-1,method:8,chunkSize:16384,windowBits:15,memLevel:8,strategy:0,to:""},t||{});var e=this.options;e.raw&&e.windowBits>0?e.windowBits=-e.windowBits:e.gzip&&e.windowBits>0&&e.windowBits<16&&(e.windowBits+=16),this.err=0,this.msg="",this.ended=!1,this.chunks=[],this.strm=new o,this.strm.avail_out=0;var i=r.deflateInit2(this.strm,e.level,e.method,e.windowBits,e.memLevel,e.strategy);if(0!==i)throw new Error(a[i]);if(e.header&&r.deflateSetHeader(this.strm,e.header),e.dictionary){var c;if(c="string"==typeof e.dictionary?n.string2buf(e.dictionary):"[object ArrayBuffer]"===l.call(e.dictionary)?new Uint8Array(e.dictionary):e.dictionary,0!==(i=r.deflateSetDictionary(this.strm,c)))throw new Error(a[i]);this._dict_set=!0}}function c(t,e){var i=new h(e);if(i.push(t,!0),i.err)throw i.msg||a[i.err];return i.result}h.prototype.push=function(t,e){var i,a,o=this.strm,h=this.options.chunkSize;if(this.ended)return!1;a=e===~~e?e:!0===e?4:0,"string"==typeof t?o.input=n.string2buf(t):"[object ArrayBuffer]"===l.call(t)?o.input=new Uint8Array(t):o.input=t,o.next_in=0,o.avail_in=o.input.length;do{if(0===o.avail_out&&(o.output=new s.Buf8(h),o.next_out=0,o.avail_out=h),1!==(i=r.deflate(o,a))&&0!==i)return this.onEnd(i),this.ended=!0,!1;0!==o.avail_out&&(0!==o.avail_in||4!==a&&2!==a)||("string"===this.options.to?this.onData(n.buf2binstring(s.shrinkBuf(o.output,o.next_out))):this.onData(s.shrinkBuf(o.output,o.next_out)))}while((o.avail_in>0||0===o.avail_out)&&1!==i);return 4===a?(i=r.deflateEnd(this.strm),this.onEnd(i),this.ended=!0,0===i):2!==a||(this.onEnd(0),o.avail_out=0,!0)},h.prototype.onData=function(t){this.chunks.push(t)},h.prototype.onEnd=function(t){0===t&&("string"===this.options.to?this.result=this.chunks.join(""):this.result=s.flattenChunks(this.chunks)),this.chunks=[],this.err=t,this.msg=this.strm.msg},e.Deflate=h,e.deflate=c,e.deflateRaw=function(t,e){return(e=e||{}).raw=!0,c(t,e)},e.gzip=function(t,e){return(e=e||{}).gzip=!0,c(t,e)}},3:(t,e,i)=>{"use strict";var r=i(663),s=i(981),n=i(972),a=i(681),o=i(834),l=i(746),h=i(670),c=Object.prototype.toString;function d(t){if(!(this instanceof d))return new d(t);this.options=s.assign({chunkSize:16384,windowBits:0,to:""},t||{});var e=this.options;e.raw&&e.windowBits>=0&&e.windowBits<16&&(e.windowBits=-e.windowBits,0===e.windowBits&&(e.windowBits=-15)),!(e.windowBits>=0&&e.windowBits<16)||t&&t.windowBits||(e.windowBits+=32),e.windowBits>15&&e.windowBits<48&&(15&e.windowBits||(e.windowBits|=15)),this.err=0,this.msg="",this.ended=!1,this.chunks=[],this.strm=new l,this.strm.avail_out=0;var i=r.inflateInit2(this.strm,e.windowBits);if(i!==a.Z_OK)throw new Error(o[i]);if(this.header=new h,r.inflateGetHeader(this.strm,this.header),e.dictionary&&("string"==typeof e.dictionary?e.dictionary=n.string2buf(e.dictionary):"[object ArrayBuffer]"===c.call(e.dictionary)&&(e.dictionary=new Uint8Array(e.dictionary)),e.raw&&(i=r.inflateSetDictionary(this.strm,e.dictionary))!==a.Z_OK))throw new Error(o[i])}function u(t,e){var i=new d(e);if(i.push(t,!0),i.err)throw i.msg||o[i.err];return i.result}d.prototype.push=function(t,e){var i,o,l,h,d,u=this.strm,f=this.options.chunkSize,p=this.options.dictionary,g=!1;if(this.ended)return!1;o=e===~~e?e:!0===e?a.Z_FINISH:a.Z_NO_FLUSH,"string"==typeof t?u.input=n.binstring2buf(t):"[object ArrayBuffer]"===c.call(t)?u.input=new Uint8Array(t):u.input=t,u.next_in=0,u.avail_in=u.input.length;do{if(0===u.avail_out&&(u.output=new s.Buf8(f),u.next_out=0,u.avail_out=f),(i=r.inflate(u,a.Z_NO_FLUSH))===a.Z_NEED_DICT&&p&&(i=r.inflateSetDictionary(this.strm,p)),i===a.Z_BUF_ERROR&&!0===g&&(i=a.Z_OK,g=!1),i!==a.Z_STREAM_END&&i!==a.Z_OK)return this.onEnd(i),this.ended=!0,!1;u.next_out&&(0!==u.avail_out&&i!==a.Z_STREAM_END&&(0!==u.avail_in||o!==a.Z_FINISH&&o!==a.Z_SYNC_FLUSH)||("string"===this.options.to?(l=n.utf8border(u.output,u.next_out),h=u.next_out-l,d=n.buf2string(u.output,l),u.next_out=h,u.avail_out=f-h,h&&s.arraySet(u.output,u.output,l,h,0),this.onData(d)):this.onData(s.shrinkBuf(u.output,u.next_out)))),0===u.avail_in&&0===u.avail_out&&(g=!0)}while((u.avail_in>0||0===u.avail_out)&&i!==a.Z_STREAM_END);return i===a.Z_STREAM_END&&(o=a.Z_FINISH),o===a.Z_FINISH?(i=r.inflateEnd(this.strm),this.onEnd(i),this.ended=!0,i===a.Z_OK):o!==a.Z_SYNC_FLUSH||(this.onEnd(a.Z_OK),u.avail_out=0,!0)},d.prototype.onData=function(t){this.chunks.push(t)},d.prototype.onEnd=function(t){t===a.Z_OK&&("string"===this.options.to?this.result=this.chunks.join(""):this.result=s.flattenChunks(this.chunks)),this.chunks=[],this.err=t,this.msg=this.strm.msg},e.Inflate=d,e.inflate=u,e.inflateRaw=function(t,e){return(e=e||{}).raw=!0,u(t,e)},e.ungzip=u},981:(t,e)=>{"use strict";var i="undefined"!=typeof Uint8Array&&"undefined"!=typeof Uint16Array&&"undefined"!=typeof Int32Array;function r(t,e){return Object.prototype.hasOwnProperty.call(t,e)}e.assign=function(t){for(var e=Array.prototype.slice.call(arguments,1);e.length;){var i=e.shift();if(i){if("object"!=typeof i)throw new TypeError(i+"must be non-object");for(var s in i)r(i,s)&&(t[s]=i[s])}}return t},e.shrinkBuf=function(t,e){return t.length===e?t:t.subarray?t.subarray(0,e):(t.length=e,t)};var s={arraySet:function(t,e,i,r,s){if(e.subarray&&t.subarray)t.set(e.subarray(i,i+r),s);else for(var n=0;n{"use strict";var r=i(981),s=!0,n=!0;try{String.fromCharCode.apply(null,[0])}catch(t){s=!1}try{String.fromCharCode.apply(null,new Uint8Array(1))}catch(t){n=!1}for(var a=new r.Buf8(256),o=0;o<256;o++)a[o]=o>=252?6:o>=248?5:o>=240?4:o>=224?3:o>=192?2:1;function l(t,e){if(e<65534&&(t.subarray&&n||!t.subarray&&s))return String.fromCharCode.apply(null,r.shrinkBuf(t,e));for(var i="",a=0;a>>6,e[a++]=128|63&i):i<65536?(e[a++]=224|i>>>12,e[a++]=128|i>>>6&63,e[a++]=128|63&i):(e[a++]=240|i>>>18,e[a++]=128|i>>>12&63,e[a++]=128|i>>>6&63,e[a++]=128|63&i);return e},e.buf2binstring=function(t){return l(t,t.length)},e.binstring2buf=function(t){for(var e=new r.Buf8(t.length),i=0,s=e.length;i4)h[r++]=65533,i+=n-1;else{for(s&=2===n?31:3===n?15:7;n>1&&i1?h[r++]=65533:s<65536?h[r++]=s:(s-=65536,h[r++]=55296|s>>10&1023,h[r++]=56320|1023&s)}return l(h,r)},e.utf8border=function(t,e){var i;for((e=e||t.length)>t.length&&(e=t.length),i=e-1;i>=0&&128==(192&t[i]);)i--;return i<0||0===i?e:i+a[t[i]]>e?i:e}},701:t=>{"use strict";t.exports=function(t,e,i,r){for(var s=65535&t,n=t>>>16&65535,a=0;0!==i;){i-=a=i>2e3?2e3:i;do{n=n+(s=s+e[r++]|0)|0}while(--a);s%=65521,n%=65521}return s|n<<16}},681:t=>{"use strict";t.exports={Z_NO_FLUSH:0,Z_PARTIAL_FLUSH:1,Z_SYNC_FLUSH:2,Z_FULL_FLUSH:3,Z_FINISH:4,Z_BLOCK:5,Z_TREES:6,Z_OK:0,Z_STREAM_END:1,Z_NEED_DICT:2,Z_ERRNO:-1,Z_STREAM_ERROR:-2,Z_DATA_ERROR:-3,Z_BUF_ERROR:-5,Z_NO_COMPRESSION:0,Z_BEST_SPEED:1,Z_BEST_COMPRESSION:9,Z_DEFAULT_COMPRESSION:-1,Z_FILTERED:1,Z_HUFFMAN_ONLY:2,Z_RLE:3,Z_FIXED:4,Z_DEFAULT_STRATEGY:0,Z_BINARY:0,Z_TEXT:1,Z_UNKNOWN:2,Z_DEFLATED:8}},407:t=>{"use strict";var e=function(){for(var t,e=[],i=0;i<256;i++){t=i;for(var r=0;r<8;r++)t=1&t?3988292384^t>>>1:t>>>1;e[i]=t}return e}();t.exports=function(t,i,r,s){var n=e,a=s+r;t^=-1;for(var o=s;o>>8^n[255&(t^i[o])];return~t}},107:(t,e,i)=>{"use strict";var r,s=i(981),n=i(697),a=i(701),o=i(407),l=i(834),h=-2,c=258,d=262,u=103,f=113,p=666;function g(t,e){return t.msg=l[e],e}function m(t){return(t<<1)-(t>4?9:0)}function v(t){for(var e=t.length;--e>=0;)t[e]=0}function _(t){var e=t.state,i=e.pending;i>t.avail_out&&(i=t.avail_out),0!==i&&(s.arraySet(t.output,e.pending_buf,e.pending_out,i,t.next_out),t.next_out+=i,e.pending_out+=i,t.total_out+=i,t.avail_out-=i,e.pending-=i,0===e.pending&&(e.pending_out=0))}function y(t,e){n._tr_flush_block(t,t.block_start>=0?t.block_start:-1,t.strstart-t.block_start,e),t.block_start=t.strstart,_(t.strm)}function b(t,e){t.pending_buf[t.pending++]=e}function x(t,e){t.pending_buf[t.pending++]=e>>>8&255,t.pending_buf[t.pending++]=255&e}function w(t,e){var i,r,s=t.max_chain_length,n=t.strstart,a=t.prev_length,o=t.nice_match,l=t.strstart>t.w_size-d?t.strstart-(t.w_size-d):0,h=t.window,u=t.w_mask,f=t.prev,p=t.strstart+c,g=h[n+a-1],m=h[n+a];t.prev_length>=t.good_match&&(s>>=2),o>t.lookahead&&(o=t.lookahead);do{if(h[(i=e)+a]===m&&h[i+a-1]===g&&h[i]===h[n]&&h[++i]===h[n+1]){n+=2,i++;do{}while(h[++n]===h[++i]&&h[++n]===h[++i]&&h[++n]===h[++i]&&h[++n]===h[++i]&&h[++n]===h[++i]&&h[++n]===h[++i]&&h[++n]===h[++i]&&h[++n]===h[++i]&&na){if(t.match_start=e,a=r,r>=o)break;g=h[n+a-1],m=h[n+a]}}}while((e=f[e&u])>l&&0!=--s);return a<=t.lookahead?a:t.lookahead}function A(t){var e,i,r,n,l,h,c,u,f,p,g=t.w_size;do{if(n=t.window_size-t.lookahead-t.strstart,t.strstart>=g+(g-d)){s.arraySet(t.window,t.window,g,g,0),t.match_start-=g,t.strstart-=g,t.block_start-=g,e=i=t.hash_size;do{r=t.head[--e],t.head[e]=r>=g?r-g:0}while(--i);e=i=g;do{r=t.prev[--e],t.prev[e]=r>=g?r-g:0}while(--i);n+=g}if(0===t.strm.avail_in)break;if(h=t.strm,c=t.window,u=t.strstart+t.lookahead,f=n,p=void 0,(p=h.avail_in)>f&&(p=f),i=0===p?0:(h.avail_in-=p,s.arraySet(c,h.input,h.next_in,p,u),1===h.state.wrap?h.adler=a(h.adler,c,p,u):2===h.state.wrap&&(h.adler=o(h.adler,c,p,u)),h.next_in+=p,h.total_in+=p,p),t.lookahead+=i,t.lookahead+t.insert>=3)for(l=t.strstart-t.insert,t.ins_h=t.window[l],t.ins_h=(t.ins_h<=3&&(t.ins_h=(t.ins_h<=3)if(r=n._tr_tally(t,t.strstart-t.match_start,t.match_length-3),t.lookahead-=t.match_length,t.match_length<=t.max_lazy_match&&t.lookahead>=3){t.match_length--;do{t.strstart++,t.ins_h=(t.ins_h<=3&&(t.ins_h=(t.ins_h<4096)&&(t.match_length=2)),t.prev_length>=3&&t.match_length<=t.prev_length){s=t.strstart+t.lookahead-3,r=n._tr_tally(t,t.strstart-1-t.prev_match,t.prev_length-3),t.lookahead-=t.prev_length-1,t.prev_length-=2;do{++t.strstart<=s&&(t.ins_h=(t.ins_h<15&&(o=2,r-=16),n<1||n>9||8!==i||r<8||r>15||e<0||e>9||a<0||a>4)return g(t,h);8===r&&(r=9);var l=new z;return t.state=l,l.strm=t,l.wrap=o,l.gzhead=null,l.w_bits=r,l.w_size=1<t.pending_buf_size-5&&(i=t.pending_buf_size-5);;){if(t.lookahead<=1){if(A(t),0===t.lookahead&&0===e)return 1;if(0===t.lookahead)break}t.strstart+=t.lookahead,t.lookahead=0;var r=t.block_start+i;if((0===t.strstart||t.strstart>=r)&&(t.lookahead=t.strstart-r,t.strstart=r,y(t,!1),0===t.strm.avail_out))return 1;if(t.strstart-t.block_start>=t.w_size-d&&(y(t,!1),0===t.strm.avail_out))return 1}return t.insert=0,4===e?(y(t,!0),0===t.strm.avail_out?3:4):(t.strstart>t.block_start&&(y(t,!1),t.strm.avail_out),1)})),new M(4,4,8,4,C),new M(4,5,16,8,C),new M(4,6,32,32,C),new M(4,4,16,16,S),new M(8,16,32,32,S),new M(8,16,128,128,S),new M(8,32,128,256,S),new M(32,128,258,1024,S),new M(32,258,258,4096,S)],e.deflateInit=function(t,e){return L(t,e,8,15,8,0)},e.deflateInit2=L,e.deflateReset=E,e.deflateResetKeep=T,e.deflateSetHeader=function(t,e){return t&&t.state?2!==t.state.wrap?h:(t.state.gzhead=e,0):h},e.deflate=function(t,e){var i,s,a,l;if(!t||!t.state||e>5||e<0)return t?g(t,h):h;if(s=t.state,!t.output||!t.input&&0!==t.avail_in||s.status===p&&4!==e)return g(t,0===t.avail_out?-5:h);if(s.strm=t,i=s.last_flush,s.last_flush=e,42===s.status)if(2===s.wrap)t.adler=0,b(s,31),b(s,139),b(s,8),s.gzhead?(b(s,(s.gzhead.text?1:0)+(s.gzhead.hcrc?2:0)+(s.gzhead.extra?4:0)+(s.gzhead.name?8:0)+(s.gzhead.comment?16:0)),b(s,255&s.gzhead.time),b(s,s.gzhead.time>>8&255),b(s,s.gzhead.time>>16&255),b(s,s.gzhead.time>>24&255),b(s,9===s.level?2:s.strategy>=2||s.level<2?4:0),b(s,255&s.gzhead.os),s.gzhead.extra&&s.gzhead.extra.length&&(b(s,255&s.gzhead.extra.length),b(s,s.gzhead.extra.length>>8&255)),s.gzhead.hcrc&&(t.adler=o(t.adler,s.pending_buf,s.pending,0)),s.gzindex=0,s.status=69):(b(s,0),b(s,0),b(s,0),b(s,0),b(s,0),b(s,9===s.level?2:s.strategy>=2||s.level<2?4:0),b(s,3),s.status=f);else{var d=8+(s.w_bits-8<<4)<<8;d|=(s.strategy>=2||s.level<2?0:s.level<6?1:6===s.level?2:3)<<6,0!==s.strstart&&(d|=32),d+=31-d%31,s.status=f,x(s,d),0!==s.strstart&&(x(s,t.adler>>>16),x(s,65535&t.adler)),t.adler=1}if(69===s.status)if(s.gzhead.extra){for(a=s.pending;s.gzindex<(65535&s.gzhead.extra.length)&&(s.pending!==s.pending_buf_size||(s.gzhead.hcrc&&s.pending>a&&(t.adler=o(t.adler,s.pending_buf,s.pending-a,a)),_(t),a=s.pending,s.pending!==s.pending_buf_size));)b(s,255&s.gzhead.extra[s.gzindex]),s.gzindex++;s.gzhead.hcrc&&s.pending>a&&(t.adler=o(t.adler,s.pending_buf,s.pending-a,a)),s.gzindex===s.gzhead.extra.length&&(s.gzindex=0,s.status=73)}else s.status=73;if(73===s.status)if(s.gzhead.name){a=s.pending;do{if(s.pending===s.pending_buf_size&&(s.gzhead.hcrc&&s.pending>a&&(t.adler=o(t.adler,s.pending_buf,s.pending-a,a)),_(t),a=s.pending,s.pending===s.pending_buf_size)){l=1;break}l=s.gzindexa&&(t.adler=o(t.adler,s.pending_buf,s.pending-a,a)),0===l&&(s.gzindex=0,s.status=91)}else s.status=91;if(91===s.status)if(s.gzhead.comment){a=s.pending;do{if(s.pending===s.pending_buf_size&&(s.gzhead.hcrc&&s.pending>a&&(t.adler=o(t.adler,s.pending_buf,s.pending-a,a)),_(t),a=s.pending,s.pending===s.pending_buf_size)){l=1;break}l=s.gzindexa&&(t.adler=o(t.adler,s.pending_buf,s.pending-a,a)),0===l&&(s.status=u)}else s.status=u;if(s.status===u&&(s.gzhead.hcrc?(s.pending+2>s.pending_buf_size&&_(t),s.pending+2<=s.pending_buf_size&&(b(s,255&t.adler),b(s,t.adler>>8&255),t.adler=0,s.status=f)):s.status=f),0!==s.pending){if(_(t),0===t.avail_out)return s.last_flush=-1,0}else if(0===t.avail_in&&m(e)<=m(i)&&4!==e)return g(t,-5);if(s.status===p&&0!==t.avail_in)return g(t,-5);if(0!==t.avail_in||0!==s.lookahead||0!==e&&s.status!==p){var w=2===s.strategy?function(t,e){for(var i;;){if(0===t.lookahead&&(A(t),0===t.lookahead)){if(0===e)return 1;break}if(t.match_length=0,i=n._tr_tally(t,0,t.window[t.strstart]),t.lookahead--,t.strstart++,i&&(y(t,!1),0===t.strm.avail_out))return 1}return t.insert=0,4===e?(y(t,!0),0===t.strm.avail_out?3:4):t.last_lit&&(y(t,!1),0===t.strm.avail_out)?1:2}(s,e):3===s.strategy?function(t,e){for(var i,r,s,a,o=t.window;;){if(t.lookahead<=c){if(A(t),t.lookahead<=c&&0===e)return 1;if(0===t.lookahead)break}if(t.match_length=0,t.lookahead>=3&&t.strstart>0&&(r=o[s=t.strstart-1])===o[++s]&&r===o[++s]&&r===o[++s]){a=t.strstart+c;do{}while(r===o[++s]&&r===o[++s]&&r===o[++s]&&r===o[++s]&&r===o[++s]&&r===o[++s]&&r===o[++s]&&r===o[++s]&&st.lookahead&&(t.match_length=t.lookahead)}if(t.match_length>=3?(i=n._tr_tally(t,1,t.match_length-3),t.lookahead-=t.match_length,t.strstart+=t.match_length,t.match_length=0):(i=n._tr_tally(t,0,t.window[t.strstart]),t.lookahead--,t.strstart++),i&&(y(t,!1),0===t.strm.avail_out))return 1}return t.insert=0,4===e?(y(t,!0),0===t.strm.avail_out?3:4):t.last_lit&&(y(t,!1),0===t.strm.avail_out)?1:2}(s,e):r[s.level].func(s,e);if(3!==w&&4!==w||(s.status=p),1===w||3===w)return 0===t.avail_out&&(s.last_flush=-1),0;if(2===w&&(1===e?n._tr_align(s):5!==e&&(n._tr_stored_block(s,0,0,!1),3===e&&(v(s.head),0===s.lookahead&&(s.strstart=0,s.block_start=0,s.insert=0))),_(t),0===t.avail_out))return s.last_flush=-1,0}return 4!==e?0:s.wrap<=0?1:(2===s.wrap?(b(s,255&t.adler),b(s,t.adler>>8&255),b(s,t.adler>>16&255),b(s,t.adler>>24&255),b(s,255&t.total_in),b(s,t.total_in>>8&255),b(s,t.total_in>>16&255),b(s,t.total_in>>24&255)):(x(s,t.adler>>>16),x(s,65535&t.adler)),_(t),s.wrap>0&&(s.wrap=-s.wrap),0!==s.pending?0:1)},e.deflateEnd=function(t){var e;return t&&t.state?42!==(e=t.state.status)&&69!==e&&73!==e&&91!==e&&e!==u&&e!==f&&e!==p?g(t,h):(t.state=null,e===f?g(t,-3):0):h},e.deflateSetDictionary=function(t,e){var i,r,n,o,l,c,d,u,f=e.length;if(!t||!t.state)return h;if(2===(o=(i=t.state).wrap)||1===o&&42!==i.status||i.lookahead)return h;for(1===o&&(t.adler=a(t.adler,e,f,0)),i.wrap=0,f>=i.w_size&&(0===o&&(v(i.head),i.strstart=0,i.block_start=0,i.insert=0),u=new s.Buf8(i.w_size),s.arraySet(u,e,f-i.w_size,i.w_size,0),e=u,f=i.w_size),l=t.avail_in,c=t.next_in,d=t.input,t.avail_in=f,t.next_in=0,t.input=e,A(i);i.lookahead>=3;){r=i.strstart,n=i.lookahead-2;do{i.ins_h=(i.ins_h<{"use strict";t.exports=function(){this.text=0,this.time=0,this.xflags=0,this.os=0,this.extra=null,this.extra_len=0,this.name="",this.comment="",this.hcrc=0,this.done=!1}},165:t=>{"use strict";t.exports=function(t,e){var i,r,s,n,a,o,l,h,c,d,u,f,p,g,m,v,_,y,b,x,w,A,C,S,M;i=t.state,r=t.next_in,S=t.input,s=r+(t.avail_in-5),n=t.next_out,M=t.output,a=n-(e-t.avail_out),o=n+(t.avail_out-257),l=i.dmax,h=i.wsize,c=i.whave,d=i.wnext,u=i.window,f=i.hold,p=i.bits,g=i.lencode,m=i.distcode,v=(1<>>=b=y>>>24,p-=b,0===(b=y>>>16&255))M[n++]=65535&y;else{if(!(16&b)){if(64&b){if(32&b){i.mode=12;break t}t.msg="invalid literal/length code",i.mode=30;break t}y=g[(65535&y)+(f&(1<>>=b,p-=b),p<15&&(f+=S[r++]<>>=b=y>>>24,p-=b,16&(b=y>>>16&255)){if(w=65535&y,p<(b&=15)&&(f+=S[r++]<l){t.msg="invalid distance too far back",i.mode=30;break t}if(f>>>=b,p-=b,w>(b=n-a)){if((b=w-b)>c&&i.sane){t.msg="invalid distance too far back",i.mode=30;break t}if(A=0,C=u,0===d){if(A+=h-b,b2;)M[n++]=C[A++],M[n++]=C[A++],M[n++]=C[A++],x-=3;x&&(M[n++]=C[A++],x>1&&(M[n++]=C[A++]))}else{A=n-w;do{M[n++]=M[A++],M[n++]=M[A++],M[n++]=M[A++],x-=3}while(x>2);x&&(M[n++]=M[A++],x>1&&(M[n++]=M[A++]))}break}if(64&b){t.msg="invalid distance code",i.mode=30;break t}y=m[(65535&y)+(f&(1<>3,f&=(1<<(p-=x<<3))-1,t.next_in=r,t.next_out=n,t.avail_in=r{"use strict";var r=i(981),s=i(701),n=i(407),a=i(165),o=i(358),l=-2,h=12,c=30;function d(t){return(t>>>24&255)+(t>>>8&65280)+((65280&t)<<8)+((255&t)<<24)}function u(){this.mode=0,this.last=!1,this.wrap=0,this.havedict=!1,this.flags=0,this.dmax=0,this.check=0,this.total=0,this.head=null,this.wbits=0,this.wsize=0,this.whave=0,this.wnext=0,this.window=null,this.hold=0,this.bits=0,this.length=0,this.offset=0,this.extra=0,this.lencode=null,this.distcode=null,this.lenbits=0,this.distbits=0,this.ncode=0,this.nlen=0,this.ndist=0,this.have=0,this.next=null,this.lens=new r.Buf16(320),this.work=new r.Buf16(288),this.lendyn=null,this.distdyn=null,this.sane=0,this.back=0,this.was=0}function f(t){var e;return t&&t.state?(e=t.state,t.total_in=t.total_out=e.total=0,t.msg="",e.wrap&&(t.adler=1&e.wrap),e.mode=1,e.last=0,e.havedict=0,e.dmax=32768,e.head=null,e.hold=0,e.bits=0,e.lencode=e.lendyn=new r.Buf32(852),e.distcode=e.distdyn=new r.Buf32(592),e.sane=1,e.back=-1,0):l}function p(t){var e;return t&&t.state?((e=t.state).wsize=0,e.whave=0,e.wnext=0,f(t)):l}function g(t,e){var i,r;return t&&t.state?(r=t.state,e<0?(i=0,e=-e):(i=1+(e>>4),e<48&&(e&=15)),e&&(e<8||e>15)?l:(null!==r.window&&r.wbits!==e&&(r.window=null),r.wrap=i,r.wbits=e,p(t))):l}function m(t,e){var i,r;return t?(r=new u,t.state=r,r.window=null,0!==(i=g(t,e))&&(t.state=null),i):l}var v,_,y=!0;function b(t){if(y){var e;for(v=new r.Buf32(512),_=new r.Buf32(32),e=0;e<144;)t.lens[e++]=8;for(;e<256;)t.lens[e++]=9;for(;e<280;)t.lens[e++]=7;for(;e<288;)t.lens[e++]=8;for(o(1,t.lens,0,288,v,0,t.work,{bits:9}),e=0;e<32;)t.lens[e++]=5;o(2,t.lens,0,32,_,0,t.work,{bits:5}),y=!1}t.lencode=v,t.lenbits=9,t.distcode=_,t.distbits=5}function x(t,e,i,s){var n,a=t.state;return null===a.window&&(a.wsize=1<=a.wsize?(r.arraySet(a.window,e,i-a.wsize,a.wsize,0),a.wnext=0,a.whave=a.wsize):((n=a.wsize-a.wnext)>s&&(n=s),r.arraySet(a.window,e,i-s,n,a.wnext),(s-=n)?(r.arraySet(a.window,e,i-s,s,0),a.wnext=s,a.whave=a.wsize):(a.wnext+=n,a.wnext===a.wsize&&(a.wnext=0),a.whave>>8&255,i.check=n(i.check,U,2,0),_=0,y=0,i.mode=2;break}if(i.flags=0,i.head&&(i.head.done=!1),!(1&i.wrap)||(((255&_)<<8)+(_>>8))%31){t.msg="incorrect header check",i.mode=c;break}if(8!=(15&_)){t.msg="unknown compression method",i.mode=c;break}if(y-=4,O=8+(15&(_>>>=4)),0===i.wbits)i.wbits=O;else if(O>i.wbits){t.msg="invalid window size",i.mode=c;break}i.dmax=1<>8&1),512&i.flags&&(U[0]=255&_,U[1]=_>>>8&255,i.check=n(i.check,U,2,0)),_=0,y=0,i.mode=3;case 3:for(;y<32;){if(0===m)break t;m--,_+=u[p++]<>>8&255,U[2]=_>>>16&255,U[3]=_>>>24&255,i.check=n(i.check,U,4,0)),_=0,y=0,i.mode=4;case 4:for(;y<16;){if(0===m)break t;m--,_+=u[p++]<>8),512&i.flags&&(U[0]=255&_,U[1]=_>>>8&255,i.check=n(i.check,U,2,0)),_=0,y=0,i.mode=5;case 5:if(1024&i.flags){for(;y<16;){if(0===m)break t;m--,_+=u[p++]<>>8&255,i.check=n(i.check,U,2,0)),_=0,y=0}else i.head&&(i.head.extra=null);i.mode=6;case 6:if(1024&i.flags&&((C=i.length)>m&&(C=m),C&&(i.head&&(O=i.head.extra_len-i.length,i.head.extra||(i.head.extra=new Array(i.head.extra_len)),r.arraySet(i.head.extra,u,p,C,O)),512&i.flags&&(i.check=n(i.check,u,C,p)),m-=C,p+=C,i.length-=C),i.length))break t;i.length=0,i.mode=7;case 7:if(2048&i.flags){if(0===m)break t;C=0;do{O=u[p+C++],i.head&&O&&i.length<65536&&(i.head.name+=String.fromCharCode(O))}while(O&&C>9&1,i.head.done=!0),t.adler=i.check=0,i.mode=h;break;case 10:for(;y<32;){if(0===m)break t;m--,_+=u[p++]<>>=7&y,y-=7&y,i.mode=27;break}for(;y<3;){if(0===m)break t;m--,_+=u[p++]<>>=1)){case 0:i.mode=14;break;case 1:if(b(i),i.mode=20,6===e){_>>>=2,y-=2;break t}break;case 2:i.mode=17;break;case 3:t.msg="invalid block type",i.mode=c}_>>>=2,y-=2;break;case 14:for(_>>>=7&y,y-=7&y;y<32;){if(0===m)break t;m--,_+=u[p++]<>>16^65535)){t.msg="invalid stored block lengths",i.mode=c;break}if(i.length=65535&_,_=0,y=0,i.mode=15,6===e)break t;case 15:i.mode=16;case 16:if(C=i.length){if(C>m&&(C=m),C>v&&(C=v),0===C)break t;r.arraySet(f,u,p,C,g),m-=C,p+=C,v-=C,g+=C,i.length-=C;break}i.mode=h;break;case 17:for(;y<14;){if(0===m)break t;m--,_+=u[p++]<>>=5,y-=5,i.ndist=1+(31&_),_>>>=5,y-=5,i.ncode=4+(15&_),_>>>=4,y-=4,i.nlen>286||i.ndist>30){t.msg="too many length or distance symbols",i.mode=c;break}i.have=0,i.mode=18;case 18:for(;i.have>>=3,y-=3}for(;i.have<19;)i.lens[B[i.have++]]=0;if(i.lencode=i.lendyn,i.lenbits=7,k={bits:i.lenbits},D=o(0,i.lens,0,19,i.lencode,0,i.work,k),i.lenbits=k.bits,D){t.msg="invalid code lengths set",i.mode=c;break}i.have=0,i.mode=19;case 19:for(;i.have>>16&255,E=65535&P,!((z=P>>>24)<=y);){if(0===m)break t;m--,_+=u[p++]<>>=z,y-=z,i.lens[i.have++]=E;else{if(16===E){for(R=z+2;y>>=z,y-=z,0===i.have){t.msg="invalid bit length repeat",i.mode=c;break}O=i.lens[i.have-1],C=3+(3&_),_>>>=2,y-=2}else if(17===E){for(R=z+3;y>>=z)),_>>>=3,y-=3}else{for(R=z+7;y>>=z)),_>>>=7,y-=7}if(i.have+C>i.nlen+i.ndist){t.msg="invalid bit length repeat",i.mode=c;break}for(;C--;)i.lens[i.have++]=O}}if(i.mode===c)break;if(0===i.lens[256]){t.msg="invalid code -- missing end-of-block",i.mode=c;break}if(i.lenbits=9,k={bits:i.lenbits},D=o(1,i.lens,0,i.nlen,i.lencode,0,i.work,k),i.lenbits=k.bits,D){t.msg="invalid literal/lengths set",i.mode=c;break}if(i.distbits=6,i.distcode=i.distdyn,k={bits:i.distbits},D=o(2,i.lens,i.nlen,i.ndist,i.distcode,0,i.work,k),i.distbits=k.bits,D){t.msg="invalid distances set",i.mode=c;break}if(i.mode=20,6===e)break t;case 20:i.mode=21;case 21:if(m>=6&&v>=258){t.next_out=g,t.avail_out=v,t.next_in=p,t.avail_in=m,i.hold=_,i.bits=y,a(t,A),g=t.next_out,f=t.output,v=t.avail_out,p=t.next_in,u=t.input,m=t.avail_in,_=i.hold,y=i.bits,i.mode===h&&(i.back=-1);break}for(i.back=0;T=(P=i.lencode[_&(1<>>16&255,E=65535&P,!((z=P>>>24)<=y);){if(0===m)break t;m--,_+=u[p++]<>L)])>>>16&255,E=65535&P,!(L+(z=P>>>24)<=y);){if(0===m)break t;m--,_+=u[p++]<>>=L,y-=L,i.back+=L}if(_>>>=z,y-=z,i.back+=z,i.length=E,0===T){i.mode=26;break}if(32&T){i.back=-1,i.mode=h;break}if(64&T){t.msg="invalid literal/length code",i.mode=c;break}i.extra=15&T,i.mode=22;case 22:if(i.extra){for(R=i.extra;y>>=i.extra,y-=i.extra,i.back+=i.extra}i.was=i.length,i.mode=23;case 23:for(;T=(P=i.distcode[_&(1<>>16&255,E=65535&P,!((z=P>>>24)<=y);){if(0===m)break t;m--,_+=u[p++]<>L)])>>>16&255,E=65535&P,!(L+(z=P>>>24)<=y);){if(0===m)break t;m--,_+=u[p++]<>>=L,y-=L,i.back+=L}if(_>>>=z,y-=z,i.back+=z,64&T){t.msg="invalid distance code",i.mode=c;break}i.offset=E,i.extra=15&T,i.mode=24;case 24:if(i.extra){for(R=i.extra;y>>=i.extra,y-=i.extra,i.back+=i.extra}if(i.offset>i.dmax){t.msg="invalid distance too far back",i.mode=c;break}i.mode=25;case 25:if(0===v)break t;if(C=A-v,i.offset>C){if((C=i.offset-C)>i.whave&&i.sane){t.msg="invalid distance too far back",i.mode=c;break}C>i.wnext?(C-=i.wnext,S=i.wsize-C):S=i.wnext-C,C>i.length&&(C=i.length),M=i.window}else M=f,S=g-i.offset,C=i.length;C>v&&(C=v),v-=C,i.length-=C;do{f[g++]=M[S++]}while(--C);0===i.length&&(i.mode=21);break;case 26:if(0===v)break t;f[g++]=i.length,v--,i.mode=21;break;case 27:if(i.wrap){for(;y<32;){if(0===m)break t;m--,_|=u[p++]<{"use strict";var r=i(981),s=15,n=[3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,115,131,163,195,227,258,0,0],a=[16,16,16,16,16,16,16,16,17,17,17,17,18,18,18,18,19,19,19,19,20,20,20,20,21,21,21,21,16,72,78],o=[1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,0,0],l=[16,16,16,16,17,17,18,18,19,19,20,20,21,21,22,22,23,23,24,24,25,25,26,26,27,27,28,28,29,29,64,64];t.exports=function(t,e,i,h,c,d,u,f){var p,g,m,v,_,y,b,x,w,A=f.bits,C=0,S=0,M=0,z=0,T=0,E=0,L=0,F=0,I=0,O=0,D=null,k=0,R=new r.Buf16(16),P=new r.Buf16(16),U=null,B=0;for(C=0;C<=s;C++)R[C]=0;for(S=0;S=1&&0===R[z];z--);if(T>z&&(T=z),0===z)return c[d++]=20971520,c[d++]=20971520,f.bits=1,0;for(M=1;M0&&(0===t||1!==z))return-1;for(P[1]=0,C=1;C852||2===t&&I>592)return 1;for(;;){b=C-L,u[S]y?(x=U[B+u[S]],w=D[k+u[S]]):(x=96,w=0),p=1<>L)+(g-=p)]=b<<24|x<<16|w}while(0!==g);for(p=1<>=1;if(0!==p?(O&=p-1,O+=p):O=0,S++,0==--R[C]){if(C===z)break;C=e[i+u[S]]}if(C>T&&(O&v)!==m){for(0===L&&(L=T),_+=M,F=1<<(E=C-L);E+L852||2===t&&I>592)return 1;c[m=O&v]=T<<24|E<<16|_-d}}return 0!==O&&(c[_+O]=C-L<<24|64<<16),f.bits=T,0}},834:t=>{"use strict";t.exports={2:"need dictionary",1:"stream end",0:"","-1":"file error","-2":"stream error","-3":"data error","-4":"insufficient memory","-5":"buffer error","-6":"incompatible version"}},697:(t,e,i)=>{"use strict";var r=i(981);function s(t){for(var e=t.length;--e>=0;)t[e]=0}var n=256,a=286,o=30,l=15,h=[0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0],c=[0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13],d=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7],u=[16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15],f=new Array(576);s(f);var p=new Array(60);s(p);var g=new Array(512);s(g);var m=new Array(256);s(m);var v=new Array(29);s(v);var _,y,b,x=new Array(o);function w(t,e,i,r,s){this.static_tree=t,this.extra_bits=e,this.extra_base=i,this.elems=r,this.max_length=s,this.has_stree=t&&t.length}function A(t,e){this.dyn_tree=t,this.max_code=0,this.stat_desc=e}function C(t){return t<256?g[t]:g[256+(t>>>7)]}function S(t,e){t.pending_buf[t.pending++]=255&e,t.pending_buf[t.pending++]=e>>>8&255}function M(t,e,i){t.bi_valid>16-i?(t.bi_buf|=e<>16-t.bi_valid,t.bi_valid+=i-16):(t.bi_buf|=e<>>=1,i<<=1}while(--e>0);return i>>>1}function E(t,e,i){var r,s,n=new Array(16),a=0;for(r=1;r<=l;r++)n[r]=a=a+i[r-1]<<1;for(s=0;s<=e;s++){var o=t[2*s+1];0!==o&&(t[2*s]=T(n[o]++,o))}}function L(t){var e;for(e=0;e8?S(t,t.bi_buf):t.bi_valid>0&&(t.pending_buf[t.pending++]=t.bi_buf),t.bi_buf=0,t.bi_valid=0}function I(t,e,i,r){var s=2*e,n=2*i;return t[s]>1;i>=1;i--)O(t,n,i);s=h;do{i=t.heap[1],t.heap[1]=t.heap[t.heap_len--],O(t,n,1),r=t.heap[1],t.heap[--t.heap_max]=i,t.heap[--t.heap_max]=r,n[2*s]=n[2*i]+n[2*r],t.depth[s]=(t.depth[i]>=t.depth[r]?t.depth[i]:t.depth[r])+1,n[2*i+1]=n[2*r+1]=s,t.heap[1]=s++,O(t,n,1)}while(t.heap_len>=2);t.heap[--t.heap_max]=t.heap[1],function(t,e){var i,r,s,n,a,o,h=e.dyn_tree,c=e.max_code,d=e.stat_desc.static_tree,u=e.stat_desc.has_stree,f=e.stat_desc.extra_bits,p=e.stat_desc.extra_base,g=e.stat_desc.max_length,m=0;for(n=0;n<=l;n++)t.bl_count[n]=0;for(h[2*t.heap[t.heap_max]+1]=0,i=t.heap_max+1;i<573;i++)(n=h[2*h[2*(r=t.heap[i])+1]+1]+1)>g&&(n=g,m++),h[2*r+1]=n,r>c||(t.bl_count[n]++,a=0,r>=p&&(a=f[r-p]),o=h[2*r],t.opt_len+=o*(n+a),u&&(t.static_len+=o*(d[2*r+1]+a)));if(0!==m){do{for(n=g-1;0===t.bl_count[n];)n--;t.bl_count[n]--,t.bl_count[n+1]+=2,t.bl_count[g]--,m-=2}while(m>0);for(n=g;0!==n;n--)for(r=t.bl_count[n];0!==r;)(s=t.heap[--i])>c||(h[2*s+1]!==n&&(t.opt_len+=(n-h[2*s+1])*h[2*s],h[2*s+1]=n),r--)}}(t,e),E(n,c,t.bl_count)}function R(t,e,i){var r,s,n=-1,a=e[1],o=0,l=7,h=4;for(0===a&&(l=138,h=3),e[2*(i+1)+1]=65535,r=0;r<=i;r++)s=a,a=e[2*(r+1)+1],++o>=7;r0?(2===t.strm.data_type&&(t.strm.data_type=function(t){var e,i=4093624447;for(e=0;e<=31;e++,i>>>=1)if(1&i&&0!==t.dyn_ltree[2*e])return 0;if(0!==t.dyn_ltree[18]||0!==t.dyn_ltree[20]||0!==t.dyn_ltree[26])return 1;for(e=32;e=3&&0===t.bl_tree[2*u[e]+1];e--);return t.opt_len+=3*(e+1)+5+5+4,e}(t),s=t.opt_len+3+7>>>3,(a=t.static_len+3+7>>>3)<=s&&(s=a)):s=a=i+5,i+4<=s&&-1!==e?B(t,e,i,r):4===t.strategy||a===s?(M(t,2+(r?1:0),3),D(t,f,p)):(M(t,4+(r?1:0),3),function(t,e,i,r){var s;for(M(t,e-257,5),M(t,i-1,5),M(t,r-4,4),s=0;s>>8&255,t.pending_buf[t.d_buf+2*t.last_lit+1]=255&e,t.pending_buf[t.l_buf+t.last_lit]=255&i,t.last_lit++,0===e?t.dyn_ltree[2*i]++:(t.matches++,e--,t.dyn_ltree[2*(m[i]+n+1)]++,t.dyn_dtree[2*C(e)]++),t.last_lit===t.lit_bufsize-1},e._tr_align=function(t){M(t,2,3),z(t,256,f),function(t){16===t.bi_valid?(S(t,t.bi_buf),t.bi_buf=0,t.bi_valid=0):t.bi_valid>=8&&(t.pending_buf[t.pending++]=255&t.bi_buf,t.bi_buf>>=8,t.bi_valid-=8)}(t)}},746:t=>{"use strict";t.exports=function(){this.input=null,this.next_in=0,this.avail_in=0,this.total_in=0,this.output=null,this.next_out=0,this.avail_out=0,this.total_out=0,this.msg="",this.state=null,this.data_type=2,this.adler=0}},185:()=>{$3Dmol.workerString=function(){self.onmessage=function(t){var e=t.data,i=e.type;if(i<0)self.atomData=e.atoms,self.volume=e.volume,self.ps=new ProteinSurface;else{var r=self.ps;r.initparm(e.expandedExtent,1!=i,self.volume),r.fillvoxels(self.atomData,e.extendedAtoms),r.buildboundary(),4!==i&&2!==i||(r.fastdistancemap(),r.boundingatom(!1),r.fillvoxelswaals(self.atomData,e.extendedAtoms)),r.marchingcube(i);var s=r.getFacesAndVertices(e.atomsToShow);self.postMessage(s)}}}.toString().replace(/(^.*?\{|\}$)/g,""),$3Dmol.workerString+=";\nfunction _classCallCheck() {};",$3Dmol.workerString+=";\n"+$3Dmol.Vector3.toString(),$3Dmol.workerString+=";\n"+$3Dmol.MarchingCubeInitializer.toString()+";\n\n",$3Dmol.workerString+=";\n"+$3Dmol.PointGrid.toString()+";\n",$3Dmol.workerString+=";\nvar ProteinSurface = "+$3Dmol.ProteinSurface.toString()+";\n",$3Dmol.SurfaceWorker=window.URL?window.URL.createObjectURL(new Blob([$3Dmol.workerString],{type:"text/javascript"})):void 0},307:t=>{"object"==typeof t.exports&&(t.exports=window.$3Dmol)},471:function(t,e){!function(t){"use strict";function e(t,e,i){for(var r=(t.byteLength,0),s=i.length;s>r;r++){var n=i.charCodeAt(r);if(128>n)t.setUint8(e++,n>>>0&127);else if(2048>n)t.setUint8(e++,n>>>6&31|192),t.setUint8(e++,n>>>0&63|128);else if(65536>n)t.setUint8(e++,n>>>12&15|224),t.setUint8(e++,n>>>6&63|128),t.setUint8(e++,n>>>0&63|128);else{if(!(1114112>n))throw new Error("bad codepoint "+n);t.setUint8(e++,n>>>18&7|240),t.setUint8(e++,n>>>12&63|128),t.setUint8(e++,n>>>6&63|128),t.setUint8(e++,n>>>0&63|128)}}}function i(t){for(var e=0,i=0,r=t.length;r>i;i++){var s=t.charCodeAt(i);if(128>s)e+=1;else if(2048>s)e+=2;else if(65536>s)e+=3;else{if(!(1114112>s))throw new Error("bad codepoint "+s);e+=4}}return e}function r(t,s,n){var a=typeof t;if("string"===a){if(32>(o=i(t)))return s.setUint8(n,160|o),e(s,n+1,t),1+o;if(256>o)return s.setUint8(n,217),s.setUint8(n+1,o),e(s,n+2,t),2+o;if(65536>o)return s.setUint8(n,218),s.setUint16(n+1,o),e(s,n+3,t),3+o;if(4294967296>o)return s.setUint8(n,219),s.setUint32(n+1,o),e(s,n+5,t),5+o}if(t instanceof Uint8Array){var o=t.byteLength,l=new Uint8Array(s.buffer);if(256>o)return s.setUint8(n,196),s.setUint8(n+1,o),l.set(t,n+2),2+o;if(65536>o)return s.setUint8(n,197),s.setUint16(n+1,o),l.set(t,n+3),3+o;if(4294967296>o)return s.setUint8(n,198),s.setUint32(n+1,o),l.set(t,n+5),5+o}if("number"===a){if(!isFinite(t))throw new Error("Number not finite: "+t);if(Math.floor(t)!==t)return s.setUint8(n,203),s.setFloat64(n+1,t),9;if(t>=0){if(128>t)return s.setUint8(n,t),1;if(256>t)return s.setUint8(n,204),s.setUint8(n+1,t),2;if(65536>t)return s.setUint8(n,205),s.setUint16(n+1,t),3;if(4294967296>t)return s.setUint8(n,206),s.setUint32(n+1,t),5;throw new Error("Number too big 0x"+t.toString(16))}if(t>=-32)return s.setInt8(n,t),1;if(t>=-128)return s.setUint8(n,208),s.setInt8(n+1,t),2;if(t>=-32768)return s.setUint8(n,209),s.setInt16(n+1,t),3;if(t>=-2147483648)return s.setUint8(n,210),s.setInt32(n+1,t),5;throw new Error("Number too small -0x"+(-t).toString(16).substr(1))}if(null===t)return s.setUint8(n,192),1;if("boolean"===a)return s.setUint8(n,t?195:194),1;if("object"===a){var h=0,c=Array.isArray(t);if(c)o=t.length;else{var d=Object.keys(t);o=d.length}if(16>o?(s.setUint8(n,o|(c?144:128)),h=1):65536>o?(s.setUint8(n,c?220:222),s.setUint16(n+1,o),h=3):4294967296>o&&(s.setUint8(n,c?221:223),s.setUint32(n+1,o),h=5),c)for(var u=0;o>u;u++)h+=r(t[u],s,n+h);else for(u=0;o>u;u++){var f=d[u];h+=r(f,s,n+h),h+=r(t[f],s,n+h)}return h}throw new Error("Unknown type "+a)}function s(t){var e=typeof t;if("string"===e){if(32>(r=i(t)))return 1+r;if(256>r)return 2+r;if(65536>r)return 3+r;if(4294967296>r)return 5+r}if(t instanceof Uint8Array){if(256>(r=t.byteLength))return 2+r;if(65536>r)return 3+r;if(4294967296>r)return 5+r}if("number"===e){if(Math.floor(t)!==t)return 9;if(t>=0){if(128>t)return 1;if(256>t)return 2;if(65536>t)return 3;if(4294967296>t)return 5;throw new Error("Number too big 0x"+t.toString(16))}if(t>=-32)return 1;if(t>=-128)return 2;if(t>=-32768)return 3;if(t>=-2147483648)return 5;throw new Error("Number too small -0x"+t.toString(16).substr(1))}if("boolean"===e||null===t)return 1;if("object"===e){var r,n=0;if(Array.isArray(t)){r=t.length;for(var a=0;r>a;a++)n+=s(t[a])}else{var o=Object.keys(t);for(r=o.length,a=0;r>a;a++){var l=o[a];n+=s(l)+s(t[l])}}if(16>r)return 1+n;if(65536>r)return 3+n;if(4294967296>r)return 5+n;throw new Error("Array or object too long 0x"+r.toString(16))}throw new Error("Unknown type "+e)}function n(t){var e=new ArrayBuffer(s(t));return r(t,new DataView(e),0),new Uint8Array(e)}function a(t,e,i){return e?new t(e.buffer,e.byteOffset,e.byteLength/(i||1)):void 0}function o(t){return a(DataView,t)}function l(t){return a(Uint8Array,t)}function h(t){return a(Int8Array,t)}function c(t){return a(Int32Array,t,4)}function d(t){return a(Float32Array,t,4)}function u(t,e){var i=t.length/2;e||(e=new Int16Array(i));for(var r=0,s=0;i>r;++r,s+=2)e[r]=t[s]<<8^t[s+1];return e}function f(t,e){var i=t.length;e||(e=new Uint8Array(2*i));for(var r=o(e),s=0;i>s;++s)r.setInt16(2*s,t[s]);return l(e)}function p(t,e){var i=t.length/4;e||(e=new Int32Array(i));for(var r=0,s=0;i>r;++r,s+=4)e[r]=t[s]<<24^t[s+1]<<16^t[s+2]<<8^t[s+3];return e}function g(t,e){var i=t.length;e||(e=new Uint8Array(4*i));for(var r=o(e),s=0;i>s;++s)r.setInt32(4*s,t[s]);return l(e)}function m(t,e){var i=t.length;e||(e=new Float32Array(i/4));for(var r=o(e),s=o(t),n=0,a=0,l=i/4;l>n;++n,a+=4)r.setFloat32(a,s.getFloat32(a),!0);return e}function v(t,e,i){var r=t.length,s=1/e;i||(i=new Float32Array(r));for(var n=0;r>n;++n)i[n]=t[n]*s;return i}function _(t,e,i){var r=t.length;i||(i=new Int32Array(r));for(var s=0;r>s;++s)i[s]=Math.round(t[s]*e);return i}function y(t,e){var i,r;if(!e){var s=0;for(i=0,r=t.length;r>i;i+=2)s+=t[i+1];e=new t.constructor(s)}var n=0;for(i=0,r=t.length;r>i;i+=2)for(var a=t[i],o=t[i+1],l=0;o>l;++l)e[n]=a,++n;return e}function b(t){if(0===t.length)return new Int32Array;var e,i,r=2;for(e=1,i=t.length;i>e;++e)t[e-1]!==t[e]&&(r+=2);var s=new Int32Array(r),n=0,a=1;for(e=1,i=t.length;i>e;++e)t[e-1]!==t[e]?(s[n]=t[e-1],s[n+1]=a,a=1,n+=2):++a;return s[n]=t[t.length-1],s[n+1]=a,s}function x(t,e){var i=t.length;e||(e=new t.constructor(i)),i&&(e[0]=t[0]);for(var r=1;i>r;++r)e[r]=t[r]+e[r-1];return e}function w(t,e){var i=t.length;e||(e=new t.constructor(i)),e[0]=t[0];for(var r=1;i>r;++r)e[r]=t[r]-t[r-1];return e}function A(t,e){var i,r,s=t instanceof Int8Array?127:32767,n=-s-1,a=t.length;if(!e){var o=0;for(i=0;a>i;++i)t[i]n&&++o;e=new Int32Array(o)}for(i=0,r=0;a>i;){for(var l=0;t[i]===s||t[i]===n;)l+=t[i],++i;l+=t[i],++i,e[r]=l,++r}return e}function C(t,e){var i,r=e?127:32767,s=-r-1,n=t.length,a=0;for(i=0;n>i;++i)0===(h=t[i])?++a:h>0?(a+=Math.ceil(h/r),h%r==0&&(a+=1)):(a+=Math.ceil(h/s),h%s==0&&(a+=1));var o=e?new Int8Array(a):new Int16Array(a),l=0;for(i=0;n>i;++i){var h;if((h=t[i])>=0)for(;h>=r;)o[l]=r,++l,h-=r;else for(;s>=h;)o[l]=s,++l,h-=s;o[l]=h,++l}return o}function S(t,e){return x(y(t),e)}function M(t){return b(w(t))}function z(t,e,i){return v(y(t,c(i)),e,i)}function T(t,e){return b(_(t,e))}function E(t,e,i){return v(x(t,c(i)),e,i)}function L(t,e,i){return w(_(t,e),i)}function F(t,e,i){return v(A(t,c(i)),e,i)}function I(t,e,i){var r=A(t,c(i));return E(r,e,d(r))}function O(t,e,i){return C(L(t,e),i)}function D(t){var e=o(t),i=e.getInt32(0),r=e.getInt32(4),s=t.subarray(8,12);return[i,t=t.subarray(12),r,s]}function k(t,e,i,r){var s=new ArrayBuffer(12+r.byteLength),n=new Uint8Array(s),a=new DataView(s);return a.setInt32(0,t),a.setInt32(4,e),i&&n.set(i,8),n.set(r,12),n}function R(t){return k(2,t.length,void 0,l(t))}function P(t){return k(4,t.length,void 0,g(t))}function U(t,e){return k(5,t.length/e,g([e]),l(t))}function B(t){return k(6,t.length,void 0,g(b(t)))}function N(t){return k(8,t.length,void 0,g(M(t)))}function G(t,e){return k(9,t.length,g([e]),g(T(t,e)))}function V(t,e){return k(10,t.length,g([e]),f(O(t,e)))}function j(t){var e={};return tt.forEach((function(i){void 0!==t[i]&&(e[i]=t[i])})),t.bondAtomList&&(e.bondAtomList=P(t.bondAtomList)),t.bondOrderList&&(e.bondOrderList=R(t.bondOrderList)),e.xCoordList=V(t.xCoordList,1e3),e.yCoordList=V(t.yCoordList,1e3),e.zCoordList=V(t.zCoordList,1e3),t.bFactorList&&(e.bFactorList=V(t.bFactorList,100)),t.atomIdList&&(e.atomIdList=N(t.atomIdList)),t.altLocList&&(e.altLocList=B(t.altLocList)),t.occupancyList&&(e.occupancyList=G(t.occupancyList,100)),e.groupIdList=N(t.groupIdList),e.groupTypeList=P(t.groupTypeList),t.secStructList&&(e.secStructList=R(t.secStructList)),t.insCodeList&&(e.insCodeList=B(t.insCodeList)),t.sequenceIndexList&&(e.sequenceIndexList=N(t.sequenceIndexList)),e.chainIdList=U(t.chainIdList,4),t.chainNameList&&(e.chainNameList=U(t.chainNameList,4)),e}function H(t){function e(t){for(var e={},i=0;t>i;i++)e[n()]=n();return e}function i(e){var i=t.subarray(a,a+e);return a+=e,i}function r(e){var i=t.subarray(a,a+e);a+=e;var r=65535;if(e>r){for(var s=[],n=0;ni;i++)e[i]=n();return e}function n(){var n,l,h=t[a];if(!(128&h))return a++,h;if(128==(240&h))return a++,e(l=15&h);if(144==(240&h))return a++,s(l=15&h);if(160==(224&h))return a++,r(l=31&h);if(!(224&~h))return n=o.getInt8(a),a++,n;switch(h){case 192:return a++,null;case 194:return a++,!1;case 195:return a++,!0;case 196:return l=o.getUint8(a+1),a+=2,i(l);case 197:return l=o.getUint16(a+1),a+=3,i(l);case 198:return l=o.getUint32(a+1),a+=5,i(l);case 202:return n=o.getFloat32(a+1),a+=5,n;case 203:return n=o.getFloat64(a+1),a+=9,n;case 204:return n=t[a+1],a+=2,n;case 205:return n=o.getUint16(a+1),a+=3,n;case 206:return n=o.getUint32(a+1),a+=5,n;case 208:return n=o.getInt8(a+1),a+=2,n;case 209:return n=o.getInt16(a+1),a+=3,n;case 210:return n=o.getInt32(a+1),a+=5,n;case 217:return l=o.getUint8(a+1),a+=2,r(l);case 218:return l=o.getUint16(a+1),a+=3,r(l);case 219:return l=o.getUint32(a+1),a+=5,r(l);case 220:return l=o.getUint16(a+1),a+=3,s(l);case 221:return l=o.getUint32(a+1),a+=5,s(l);case 222:return l=o.getUint16(a+1),a+=3,e(l);case 223:return l=o.getUint32(a+1),a+=5,e(l)}throw new Error("Unknown type 0x"+h.toString(16))}var a=0,o=new DataView(t.buffer);return n()}function W(t,e,i,r){switch(t){case 1:return m(e);case 2:return h(e);case 3:return u(e);case 4:return p(e);case 5:return l(e);case 6:return y(p(e),new Uint8Array(i));case 7:return y(p(e));case 8:return S(p(e));case 9:return z(p(e),p(r)[0]);case 10:return I(u(e),p(r)[0]);case 11:return v(u(e),p(r)[0]);case 12:return F(u(e),p(r)[0]);case 13:return F(h(e),p(r)[0]);case 14:return A(u(e));case 15:return A(h(e))}}function q(t,e){var i=(e=e||{}).ignoreFields,r={};return it.forEach((function(e){var s=!!i&&-1!==i.indexOf(e),n=t[e];s||void 0===n||(n instanceof Uint8Array?r[e]=W.apply(null,D(n)):r[e]=n)})),r}function Y(t){return String.fromCharCode.apply(null,t).replace(/\0/g,"")}function Z(t,e,i){var r,s,n,a,o,l,h=(i=i||{}).firstModelOnly,c=e.onModel,d=e.onChain,u=e.onGroup,f=e.onAtom,p=e.onBond,g=0,m=0,v=0,_=0,y=0,b=-1,x=t.chainNameList,w=t.secStructList,A=t.insCodeList,C=t.sequenceIndexList,S=t.atomIdList,M=t.bFactorList,z=t.altLocList,T=t.occupancyList,E=t.bondAtomList,L=t.bondOrderList;for(r=0,s=t.chainsPerModel.length;s>r&&!(h&&g>0);++r){var F=t.chainsPerModel[g];for(c&&c({chainCount:F,modelIndex:g}),n=0;F>n;++n){var I=t.groupsPerChain[m];if(d){var O=Y(t.chainIdList.subarray(4*m,4*m+4)),D=null;x&&(D=Y(x.subarray(4*m,4*m+4))),d({groupCount:I,chainIndex:m,modelIndex:g,chainId:O,chainName:D})}for(a=0;I>a;++a){var k=t.groupList[t.groupTypeList[v]],R=k.atomNameList.length;if(u){var P=null;w&&(P=w[v]);var U=null;t.insCodeList&&(U=String.fromCharCode(A[v]));var B=null;C&&(B=C[v]),u({atomCount:R,groupIndex:v,chainIndex:m,modelIndex:g,groupId:t.groupIdList[v],groupType:t.groupTypeList[v],groupName:k.groupName,singleLetterCode:k.singleLetterCode,chemCompType:k.chemCompType,secStruct:P,insCode:U,sequenceIndex:B})}for(o=0;R>o;++o){if(f){var N=null;S&&(N=S[_]);var G=null;M&&(G=M[_]);var V=null;z&&(V=String.fromCharCode(z[_]));var j=null;T&&(j=T[_]),f({atomIndex:_,groupIndex:v,chainIndex:m,modelIndex:g,atomId:N,element:k.elementList[o],atomName:k.atomNameList[o],formalCharge:k.formalChargeList[o],xCoord:t.xCoordList[_],yCoord:t.yCoordList[_],zCoord:t.zCoordList[_],bFactor:G,altLoc:V,occupancy:j})}_+=1}if(p){var H=k.bondAtomList;for(o=0,l=k.bondOrderList.length;l>o;++o)p({atomIndex1:_-R+H[2*o],atomIndex2:_-R+H[2*o+1],bondOrder:k.bondOrderList[o]})}v+=1}m+=1}if(y=b+1,b=_-1,p&&E)for(o=0,l=E.length;l>o;o+=2){var W=E[o],q=E[o+1];(W>=y&&b>=W||q>=y&&b>=q)&&p({atomIndex1:W,atomIndex2:q,bondOrder:L?L[o/2]:null})}g+=1}}function X(t){return n(j(t))}function K(t,e){return t instanceof ArrayBuffer&&(t=new Uint8Array(t)),q(t instanceof Uint8Array?H(t):t,e)}function Q(t,e,i,r){function s(){try{var t=K(n.response);i(t)}catch(t){r(t)}}var n=new XMLHttpRequest;n.addEventListener("load",s,!0),n.addEventListener("error",r,!0),n.responseType="arraybuffer",n.open("GET",e+t.toUpperCase()),n.send()}function $(t,e,i){Q(t,nt,e,i)}function J(t,e,i){Q(t,at,e,i)}var tt=["mmtfVersion","mmtfProducer","unitCell","spaceGroup","structureId","title","depositionDate","releaseDate","experimentalMethods","resolution","rFree","rWork","bioAssemblyList","ncsOperatorList","entityList","groupList","numBonds","numAtoms","numGroups","numChains","numModels","groupsPerChain","chainsPerModel"],et=["xCoordList","yCoordList","zCoordList","groupIdList","groupTypeList","chainIdList","bFactorList","atomIdList","altLocList","occupancyList","secStructList","insCodeList","sequenceIndexList","chainNameList","bondAtomList","bondOrderList"],it=tt.concat(et),rt="v1.0.1",st="//mmtf.rcsb.org/v1.0/",nt=st+"full/",at=st+"reduced/";t.encode=X,t.decode=K,t.traverse=Z,t.fetch=$,t.fetchReduced=J,t.version=rt,t.fetchUrl=nt,t.fetchReducedUrl=at,t.encodeMsgpack=n,t.encodeMmtf=j,t.decodeMsgpack=H,t.decodeMmtf=q}(e)},75:(t,e,i)=>{"use strict";i.r(e),i.d(e,{Deflate:()=>yi,Inflate:()=>Ai,constants:()=>zi,default:()=>Ti,deflate:()=>bi,deflateRaw:()=>xi,gzip:()=>wi,inflate:()=>Ci,inflateRaw:()=>Si,ungzip:()=>Mi});function r(t){let e=t.length;for(;--e>=0;)t[e]=0}const s=256,n=286,a=30,o=15,l=new Uint8Array([0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0]),h=new Uint8Array([0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13]),c=new Uint8Array([0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7]),d=new Uint8Array([16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15]),u=new Array(576);r(u);const f=new Array(60);r(f);const p=new Array(512);r(p);const g=new Array(256);r(g);const m=new Array(29);r(m);const v=new Array(a);function _(t,e,i,r,s){this.static_tree=t,this.extra_bits=e,this.extra_base=i,this.elems=r,this.max_length=s,this.has_stree=t&&t.length}let y,b,x;function w(t,e){this.dyn_tree=t,this.max_code=0,this.stat_desc=e}r(v);const A=t=>t<256?p[t]:p[256+(t>>>7)],C=(t,e)=>{t.pending_buf[t.pending++]=255&e,t.pending_buf[t.pending++]=e>>>8&255},S=(t,e,i)=>{t.bi_valid>16-i?(t.bi_buf|=e<>16-t.bi_valid,t.bi_valid+=i-16):(t.bi_buf|=e<{S(t,i[2*e],i[2*e+1])},z=(t,e)=>{let i=0;do{i|=1&t,t>>>=1,i<<=1}while(--e>0);return i>>>1},T=(t,e,i)=>{const r=new Array(16);let s,n,a=0;for(s=1;s<=o;s++)a=a+i[s-1]<<1,r[s]=a;for(n=0;n<=e;n++){let e=t[2*n+1];0!==e&&(t[2*n]=z(r[e]++,e))}},E=t=>{let e;for(e=0;e{t.bi_valid>8?C(t,t.bi_buf):t.bi_valid>0&&(t.pending_buf[t.pending++]=t.bi_buf),t.bi_buf=0,t.bi_valid=0},F=(t,e,i,r)=>{const s=2*e,n=2*i;return t[s]{const r=t.heap[i];let s=i<<1;for(;s<=t.heap_len&&(s{let r,n,a,o,c=0;if(0!==t.sym_next)do{r=255&t.pending_buf[t.sym_buf+c++],r+=(255&t.pending_buf[t.sym_buf+c++])<<8,n=t.pending_buf[t.sym_buf+c++],0===r?M(t,n,e):(a=g[n],M(t,a+s+1,e),o=l[a],0!==o&&(n-=m[a],S(t,n,o)),r--,a=A(r),M(t,a,i),o=h[a],0!==o&&(r-=v[a],S(t,r,o)))}while(c{const i=e.dyn_tree,r=e.stat_desc.static_tree,s=e.stat_desc.has_stree,n=e.stat_desc.elems;let a,l,h,c=-1;for(t.heap_len=0,t.heap_max=573,a=0;a>1;a>=1;a--)I(t,i,a);h=n;do{a=t.heap[1],t.heap[1]=t.heap[t.heap_len--],I(t,i,1),l=t.heap[1],t.heap[--t.heap_max]=a,t.heap[--t.heap_max]=l,i[2*h]=i[2*a]+i[2*l],t.depth[h]=(t.depth[a]>=t.depth[l]?t.depth[a]:t.depth[l])+1,i[2*a+1]=i[2*l+1]=h,t.heap[1]=h++,I(t,i,1)}while(t.heap_len>=2);t.heap[--t.heap_max]=t.heap[1],((t,e)=>{const i=e.dyn_tree,r=e.max_code,s=e.stat_desc.static_tree,n=e.stat_desc.has_stree,a=e.stat_desc.extra_bits,l=e.stat_desc.extra_base,h=e.stat_desc.max_length;let c,d,u,f,p,g,m=0;for(f=0;f<=o;f++)t.bl_count[f]=0;for(i[2*t.heap[t.heap_max]+1]=0,c=t.heap_max+1;c<573;c++)d=t.heap[c],f=i[2*i[2*d+1]+1]+1,f>h&&(f=h,m++),i[2*d+1]=f,d>r||(t.bl_count[f]++,p=0,d>=l&&(p=a[d-l]),g=i[2*d],t.opt_len+=g*(f+p),n&&(t.static_len+=g*(s[2*d+1]+p)));if(0!==m){do{for(f=h-1;0===t.bl_count[f];)f--;t.bl_count[f]--,t.bl_count[f+1]+=2,t.bl_count[h]--,m-=2}while(m>0);for(f=h;0!==f;f--)for(d=t.bl_count[f];0!==d;)u=t.heap[--c],u>r||(i[2*u+1]!==f&&(t.opt_len+=(f-i[2*u+1])*i[2*u],i[2*u+1]=f),d--)}})(t,e),T(i,c,t.bl_count)},k=(t,e,i)=>{let r,s,n=-1,a=e[1],o=0,l=7,h=4;for(0===a&&(l=138,h=3),e[2*(i+1)+1]=65535,r=0;r<=i;r++)s=a,a=e[2*(r+1)+1],++o{let r,s,n=-1,a=e[1],o=0,l=7,h=4;for(0===a&&(l=138,h=3),r=0;r<=i;r++)if(s=a,a=e[2*(r+1)+1],!(++o{S(t,0+(r?1:0),3),L(t),C(t,i),C(t,~i),i&&t.pending_buf.set(t.window.subarray(e,e+i),t.pending),t.pending+=i};var B=(t,e,i,r)=>{let n,a,o=0;t.level>0?(2===t.strm.data_type&&(t.strm.data_type=(t=>{let e,i=4093624447;for(e=0;e<=31;e++,i>>>=1)if(1&i&&0!==t.dyn_ltree[2*e])return 0;if(0!==t.dyn_ltree[18]||0!==t.dyn_ltree[20]||0!==t.dyn_ltree[26])return 1;for(e=32;e{let e;for(k(t,t.dyn_ltree,t.l_desc.max_code),k(t,t.dyn_dtree,t.d_desc.max_code),D(t,t.bl_desc),e=18;e>=3&&0===t.bl_tree[2*d[e]+1];e--);return t.opt_len+=3*(e+1)+5+5+4,e})(t),n=t.opt_len+3+7>>>3,a=t.static_len+3+7>>>3,a<=n&&(n=a)):n=a=i+5,i+4<=n&&-1!==e?U(t,e,i,r):4===t.strategy||a===n?(S(t,2+(r?1:0),3),O(t,u,f)):(S(t,4+(r?1:0),3),((t,e,i,r)=>{let s;for(S(t,e-257,5),S(t,i-1,5),S(t,r-4,4),s=0;s{P||((()=>{let t,e,i,r,s;const d=new Array(16);for(i=0,r=0;r<28;r++)for(m[r]=i,t=0;t<1<>=7;r(t.pending_buf[t.sym_buf+t.sym_next++]=e,t.pending_buf[t.sym_buf+t.sym_next++]=e>>8,t.pending_buf[t.sym_buf+t.sym_next++]=i,0===e?t.dyn_ltree[2*i]++:(t.matches++,e--,t.dyn_ltree[2*(g[i]+s+1)]++,t.dyn_dtree[2*A(e)]++),t.sym_next===t.sym_end),_tr_align:t=>{S(t,2,3),M(t,256,u),(t=>{16===t.bi_valid?(C(t,t.bi_buf),t.bi_buf=0,t.bi_valid=0):t.bi_valid>=8&&(t.pending_buf[t.pending++]=255&t.bi_buf,t.bi_buf>>=8,t.bi_valid-=8)})(t)}};var G=(t,e,i,r)=>{let s=65535&t,n=t>>>16&65535,a=0;for(;0!==i;){a=i>2e3?2e3:i,i-=a;do{s=s+e[r++]|0,n=n+s|0}while(--a);s%=65521,n%=65521}return s|n<<16};const V=new Uint32Array((()=>{let t,e=[];for(var i=0;i<256;i++){t=i;for(var r=0;r<8;r++)t=1&t?3988292384^t>>>1:t>>>1;e[i]=t}return e})());var j=(t,e,i,r)=>{const s=V,n=r+i;t^=-1;for(let i=r;i>>8^s[255&(t^e[i])];return~t},H={2:"need dictionary",1:"stream end",0:"","-1":"file error","-2":"stream error","-3":"data error","-4":"insufficient memory","-5":"buffer error","-6":"incompatible version"},W={Z_NO_FLUSH:0,Z_PARTIAL_FLUSH:1,Z_SYNC_FLUSH:2,Z_FULL_FLUSH:3,Z_FINISH:4,Z_BLOCK:5,Z_TREES:6,Z_OK:0,Z_STREAM_END:1,Z_NEED_DICT:2,Z_ERRNO:-1,Z_STREAM_ERROR:-2,Z_DATA_ERROR:-3,Z_MEM_ERROR:-4,Z_BUF_ERROR:-5,Z_NO_COMPRESSION:0,Z_BEST_SPEED:1,Z_BEST_COMPRESSION:9,Z_DEFAULT_COMPRESSION:-1,Z_FILTERED:1,Z_HUFFMAN_ONLY:2,Z_RLE:3,Z_FIXED:4,Z_DEFAULT_STRATEGY:0,Z_BINARY:0,Z_TEXT:1,Z_UNKNOWN:2,Z_DEFLATED:8};const{_tr_init:q,_tr_stored_block:Y,_tr_flush_block:Z,_tr_tally:X,_tr_align:K}=N,{Z_NO_FLUSH:Q,Z_PARTIAL_FLUSH:$,Z_FULL_FLUSH:J,Z_FINISH:tt,Z_BLOCK:et,Z_OK:it,Z_STREAM_END:rt,Z_STREAM_ERROR:st,Z_DATA_ERROR:nt,Z_BUF_ERROR:at,Z_DEFAULT_COMPRESSION:ot,Z_FILTERED:lt,Z_HUFFMAN_ONLY:ht,Z_RLE:ct,Z_FIXED:dt,Z_DEFAULT_STRATEGY:ut,Z_UNKNOWN:ft,Z_DEFLATED:pt}=W,gt=258,mt=262,vt=42,_t=113,yt=666,bt=(t,e)=>(t.msg=H[e],e),xt=t=>2*t-(t>4?9:0),wt=t=>{let e=t.length;for(;--e>=0;)t[e]=0},At=t=>{let e,i,r,s=t.w_size;e=t.hash_size,r=e;do{i=t.head[--r],t.head[r]=i>=s?i-s:0}while(--e);e=s,r=e;do{i=t.prev[--r],t.prev[r]=i>=s?i-s:0}while(--e)};let Ct=(t,e,i)=>(e<{const e=t.state;let i=e.pending;i>t.avail_out&&(i=t.avail_out),0!==i&&(t.output.set(e.pending_buf.subarray(e.pending_out,e.pending_out+i),t.next_out),t.next_out+=i,e.pending_out+=i,t.total_out+=i,t.avail_out-=i,e.pending-=i,0===e.pending&&(e.pending_out=0))},Mt=(t,e)=>{Z(t,t.block_start>=0?t.block_start:-1,t.strstart-t.block_start,e),t.block_start=t.strstart,St(t.strm)},zt=(t,e)=>{t.pending_buf[t.pending++]=e},Tt=(t,e)=>{t.pending_buf[t.pending++]=e>>>8&255,t.pending_buf[t.pending++]=255&e},Et=(t,e,i,r)=>{let s=t.avail_in;return s>r&&(s=r),0===s?0:(t.avail_in-=s,e.set(t.input.subarray(t.next_in,t.next_in+s),i),1===t.state.wrap?t.adler=G(t.adler,e,s,i):2===t.state.wrap&&(t.adler=j(t.adler,e,s,i)),t.next_in+=s,t.total_in+=s,s)},Lt=(t,e)=>{let i,r,s=t.max_chain_length,n=t.strstart,a=t.prev_length,o=t.nice_match;const l=t.strstart>t.w_size-mt?t.strstart-(t.w_size-mt):0,h=t.window,c=t.w_mask,d=t.prev,u=t.strstart+gt;let f=h[n+a-1],p=h[n+a];t.prev_length>=t.good_match&&(s>>=2),o>t.lookahead&&(o=t.lookahead);do{if(i=e,h[i+a]===p&&h[i+a-1]===f&&h[i]===h[n]&&h[++i]===h[n+1]){n+=2,i++;do{}while(h[++n]===h[++i]&&h[++n]===h[++i]&&h[++n]===h[++i]&&h[++n]===h[++i]&&h[++n]===h[++i]&&h[++n]===h[++i]&&h[++n]===h[++i]&&h[++n]===h[++i]&&na){if(t.match_start=e,a=r,r>=o)break;f=h[n+a-1],p=h[n+a]}}}while((e=d[e&c])>l&&0!=--s);return a<=t.lookahead?a:t.lookahead},Ft=t=>{const e=t.w_size;let i,r,s;do{if(r=t.window_size-t.lookahead-t.strstart,t.strstart>=e+(e-mt)&&(t.window.set(t.window.subarray(e,e+e-r),0),t.match_start-=e,t.strstart-=e,t.block_start-=e,t.insert>t.strstart&&(t.insert=t.strstart),At(t),r+=e),0===t.strm.avail_in)break;if(i=Et(t.strm,t.window,t.strstart+t.lookahead,r),t.lookahead+=i,t.lookahead+t.insert>=3)for(s=t.strstart-t.insert,t.ins_h=t.window[s],t.ins_h=Ct(t,t.ins_h,t.window[s+1]);t.insert&&(t.ins_h=Ct(t,t.ins_h,t.window[s+3-1]),t.prev[s&t.w_mask]=t.head[t.ins_h],t.head[t.ins_h]=s,s++,t.insert--,!(t.lookahead+t.insert<3)););}while(t.lookahead{let i,r,s,n=t.pending_buf_size-5>t.w_size?t.w_size:t.pending_buf_size-5,a=0,o=t.strm.avail_in;do{if(i=65535,s=t.bi_valid+42>>3,t.strm.avail_outr+t.strm.avail_in&&(i=r+t.strm.avail_in),i>s&&(i=s),i>8,t.pending_buf[t.pending-2]=~i,t.pending_buf[t.pending-1]=~i>>8,St(t.strm),r&&(r>i&&(r=i),t.strm.output.set(t.window.subarray(t.block_start,t.block_start+r),t.strm.next_out),t.strm.next_out+=r,t.strm.avail_out-=r,t.strm.total_out+=r,t.block_start+=r,i-=r),i&&(Et(t.strm,t.strm.output,t.strm.next_out,i),t.strm.next_out+=i,t.strm.avail_out-=i,t.strm.total_out+=i)}while(0===a);return o-=t.strm.avail_in,o&&(o>=t.w_size?(t.matches=2,t.window.set(t.strm.input.subarray(t.strm.next_in-t.w_size,t.strm.next_in),0),t.strstart=t.w_size,t.insert=t.strstart):(t.window_size-t.strstart<=o&&(t.strstart-=t.w_size,t.window.set(t.window.subarray(t.w_size,t.w_size+t.strstart),0),t.matches<2&&t.matches++,t.insert>t.strstart&&(t.insert=t.strstart)),t.window.set(t.strm.input.subarray(t.strm.next_in-o,t.strm.next_in),t.strstart),t.strstart+=o,t.insert+=o>t.w_size-t.insert?t.w_size-t.insert:o),t.block_start=t.strstart),t.high_waters&&t.block_start>=t.w_size&&(t.block_start-=t.w_size,t.strstart-=t.w_size,t.window.set(t.window.subarray(t.w_size,t.w_size+t.strstart),0),t.matches<2&&t.matches++,s+=t.w_size,t.insert>t.strstart&&(t.insert=t.strstart)),s>t.strm.avail_in&&(s=t.strm.avail_in),s&&(Et(t.strm,t.window,t.strstart,s),t.strstart+=s,t.insert+=s>t.w_size-t.insert?t.w_size-t.insert:s),t.high_water>3,s=t.pending_buf_size-s>65535?65535:t.pending_buf_size-s,n=s>t.w_size?t.w_size:s,r=t.strstart-t.block_start,(r>=n||(r||e===tt)&&e!==Q&&0===t.strm.avail_in&&r<=s)&&(i=r>s?s:r,a=e===tt&&0===t.strm.avail_in&&i===r?1:0,Y(t,t.block_start,i,a),t.block_start+=i,St(t.strm)),a?3:1)},Ot=(t,e)=>{let i,r;for(;;){if(t.lookahead=3&&(t.ins_h=Ct(t,t.ins_h,t.window[t.strstart+3-1]),i=t.prev[t.strstart&t.w_mask]=t.head[t.ins_h],t.head[t.ins_h]=t.strstart),0!==i&&t.strstart-i<=t.w_size-mt&&(t.match_length=Lt(t,i)),t.match_length>=3)if(r=X(t,t.strstart-t.match_start,t.match_length-3),t.lookahead-=t.match_length,t.match_length<=t.max_lazy_match&&t.lookahead>=3){t.match_length--;do{t.strstart++,t.ins_h=Ct(t,t.ins_h,t.window[t.strstart+3-1]),i=t.prev[t.strstart&t.w_mask]=t.head[t.ins_h],t.head[t.ins_h]=t.strstart}while(0!=--t.match_length);t.strstart++}else t.strstart+=t.match_length,t.match_length=0,t.ins_h=t.window[t.strstart],t.ins_h=Ct(t,t.ins_h,t.window[t.strstart+1]);else r=X(t,0,t.window[t.strstart]),t.lookahead--,t.strstart++;if(r&&(Mt(t,!1),0===t.strm.avail_out))return 1}return t.insert=t.strstart<2?t.strstart:2,e===tt?(Mt(t,!0),0===t.strm.avail_out?3:4):t.sym_next&&(Mt(t,!1),0===t.strm.avail_out)?1:2},Dt=(t,e)=>{let i,r,s;for(;;){if(t.lookahead=3&&(t.ins_h=Ct(t,t.ins_h,t.window[t.strstart+3-1]),i=t.prev[t.strstart&t.w_mask]=t.head[t.ins_h],t.head[t.ins_h]=t.strstart),t.prev_length=t.match_length,t.prev_match=t.match_start,t.match_length=2,0!==i&&t.prev_length4096)&&(t.match_length=2)),t.prev_length>=3&&t.match_length<=t.prev_length){s=t.strstart+t.lookahead-3,r=X(t,t.strstart-1-t.prev_match,t.prev_length-3),t.lookahead-=t.prev_length-1,t.prev_length-=2;do{++t.strstart<=s&&(t.ins_h=Ct(t,t.ins_h,t.window[t.strstart+3-1]),i=t.prev[t.strstart&t.w_mask]=t.head[t.ins_h],t.head[t.ins_h]=t.strstart)}while(0!=--t.prev_length);if(t.match_available=0,t.match_length=2,t.strstart++,r&&(Mt(t,!1),0===t.strm.avail_out))return 1}else if(t.match_available){if(r=X(t,0,t.window[t.strstart-1]),r&&Mt(t,!1),t.strstart++,t.lookahead--,0===t.strm.avail_out)return 1}else t.match_available=1,t.strstart++,t.lookahead--}return t.match_available&&(r=X(t,0,t.window[t.strstart-1]),t.match_available=0),t.insert=t.strstart<2?t.strstart:2,e===tt?(Mt(t,!0),0===t.strm.avail_out?3:4):t.sym_next&&(Mt(t,!1),0===t.strm.avail_out)?1:2};function kt(t,e,i,r,s){this.good_length=t,this.max_lazy=e,this.nice_length=i,this.max_chain=r,this.func=s}const Rt=[new kt(0,0,0,0,It),new kt(4,4,8,4,Ot),new kt(4,5,16,8,Ot),new kt(4,6,32,32,Ot),new kt(4,4,16,16,Dt),new kt(8,16,32,32,Dt),new kt(8,16,128,128,Dt),new kt(8,32,128,256,Dt),new kt(32,128,258,1024,Dt),new kt(32,258,258,4096,Dt)];function Pt(){this.strm=null,this.status=0,this.pending_buf=null,this.pending_buf_size=0,this.pending_out=0,this.pending=0,this.wrap=0,this.gzhead=null,this.gzindex=0,this.method=pt,this.last_flush=-1,this.w_size=0,this.w_bits=0,this.w_mask=0,this.window=null,this.window_size=0,this.prev=null,this.head=null,this.ins_h=0,this.hash_size=0,this.hash_bits=0,this.hash_mask=0,this.hash_shift=0,this.block_start=0,this.match_length=0,this.prev_match=0,this.match_available=0,this.strstart=0,this.match_start=0,this.lookahead=0,this.prev_length=0,this.max_chain_length=0,this.max_lazy_match=0,this.level=0,this.strategy=0,this.good_match=0,this.nice_match=0,this.dyn_ltree=new Uint16Array(1146),this.dyn_dtree=new Uint16Array(122),this.bl_tree=new Uint16Array(78),wt(this.dyn_ltree),wt(this.dyn_dtree),wt(this.bl_tree),this.l_desc=null,this.d_desc=null,this.bl_desc=null,this.bl_count=new Uint16Array(16),this.heap=new Uint16Array(573),wt(this.heap),this.heap_len=0,this.heap_max=0,this.depth=new Uint16Array(573),wt(this.depth),this.sym_buf=0,this.lit_bufsize=0,this.sym_next=0,this.sym_end=0,this.opt_len=0,this.static_len=0,this.matches=0,this.insert=0,this.bi_buf=0,this.bi_valid=0}const Ut=t=>{if(!t)return 1;const e=t.state;return!e||e.strm!==t||e.status!==vt&&57!==e.status&&69!==e.status&&73!==e.status&&91!==e.status&&103!==e.status&&e.status!==_t&&e.status!==yt?1:0},Bt=t=>{if(Ut(t))return bt(t,st);t.total_in=t.total_out=0,t.data_type=ft;const e=t.state;return e.pending=0,e.pending_out=0,e.wrap<0&&(e.wrap=-e.wrap),e.status=2===e.wrap?57:e.wrap?vt:_t,t.adler=2===e.wrap?0:1,e.last_flush=-2,q(e),it},Nt=t=>{const e=Bt(t);var i;return e===it&&((i=t.state).window_size=2*i.w_size,wt(i.head),i.max_lazy_match=Rt[i.level].max_lazy,i.good_match=Rt[i.level].good_length,i.nice_match=Rt[i.level].nice_length,i.max_chain_length=Rt[i.level].max_chain,i.strstart=0,i.block_start=0,i.lookahead=0,i.insert=0,i.match_length=i.prev_length=2,i.match_available=0,i.ins_h=0),e},Gt=(t,e,i,r,s,n)=>{if(!t)return st;let a=1;if(e===ot&&(e=6),r<0?(a=0,r=-r):r>15&&(a=2,r-=16),s<1||s>9||i!==pt||r<8||r>15||e<0||e>9||n<0||n>dt||8===r&&1!==a)return bt(t,st);8===r&&(r=9);const o=new Pt;return t.state=o,o.strm=t,o.status=vt,o.wrap=a,o.gzhead=null,o.w_bits=r,o.w_size=1<Gt(t,e,pt,15,8,ut),deflateInit2:Gt,deflateReset:Nt,deflateResetKeep:Bt,deflateSetHeader:(t,e)=>Ut(t)||2!==t.state.wrap?st:(t.state.gzhead=e,it),deflate:(t,e)=>{if(Ut(t)||e>et||e<0)return t?bt(t,st):st;const i=t.state;if(!t.output||0!==t.avail_in&&!t.input||i.status===yt&&e!==tt)return bt(t,0===t.avail_out?at:st);const r=i.last_flush;if(i.last_flush=e,0!==i.pending){if(St(t),0===t.avail_out)return i.last_flush=-1,it}else if(0===t.avail_in&&xt(e)<=xt(r)&&e!==tt)return bt(t,at);if(i.status===yt&&0!==t.avail_in)return bt(t,at);if(i.status===vt&&0===i.wrap&&(i.status=_t),i.status===vt){let e=pt+(i.w_bits-8<<4)<<8,r=-1;if(r=i.strategy>=ht||i.level<2?0:i.level<6?1:6===i.level?2:3,e|=r<<6,0!==i.strstart&&(e|=32),e+=31-e%31,Tt(i,e),0!==i.strstart&&(Tt(i,t.adler>>>16),Tt(i,65535&t.adler)),t.adler=1,i.status=_t,St(t),0!==i.pending)return i.last_flush=-1,it}if(57===i.status)if(t.adler=0,zt(i,31),zt(i,139),zt(i,8),i.gzhead)zt(i,(i.gzhead.text?1:0)+(i.gzhead.hcrc?2:0)+(i.gzhead.extra?4:0)+(i.gzhead.name?8:0)+(i.gzhead.comment?16:0)),zt(i,255&i.gzhead.time),zt(i,i.gzhead.time>>8&255),zt(i,i.gzhead.time>>16&255),zt(i,i.gzhead.time>>24&255),zt(i,9===i.level?2:i.strategy>=ht||i.level<2?4:0),zt(i,255&i.gzhead.os),i.gzhead.extra&&i.gzhead.extra.length&&(zt(i,255&i.gzhead.extra.length),zt(i,i.gzhead.extra.length>>8&255)),i.gzhead.hcrc&&(t.adler=j(t.adler,i.pending_buf,i.pending,0)),i.gzindex=0,i.status=69;else if(zt(i,0),zt(i,0),zt(i,0),zt(i,0),zt(i,0),zt(i,9===i.level?2:i.strategy>=ht||i.level<2?4:0),zt(i,3),i.status=_t,St(t),0!==i.pending)return i.last_flush=-1,it;if(69===i.status){if(i.gzhead.extra){let e=i.pending,r=(65535&i.gzhead.extra.length)-i.gzindex;for(;i.pending+r>i.pending_buf_size;){let s=i.pending_buf_size-i.pending;if(i.pending_buf.set(i.gzhead.extra.subarray(i.gzindex,i.gzindex+s),i.pending),i.pending=i.pending_buf_size,i.gzhead.hcrc&&i.pending>e&&(t.adler=j(t.adler,i.pending_buf,i.pending-e,e)),i.gzindex+=s,St(t),0!==i.pending)return i.last_flush=-1,it;e=0,r-=s}let s=new Uint8Array(i.gzhead.extra);i.pending_buf.set(s.subarray(i.gzindex,i.gzindex+r),i.pending),i.pending+=r,i.gzhead.hcrc&&i.pending>e&&(t.adler=j(t.adler,i.pending_buf,i.pending-e,e)),i.gzindex=0}i.status=73}if(73===i.status){if(i.gzhead.name){let e,r=i.pending;do{if(i.pending===i.pending_buf_size){if(i.gzhead.hcrc&&i.pending>r&&(t.adler=j(t.adler,i.pending_buf,i.pending-r,r)),St(t),0!==i.pending)return i.last_flush=-1,it;r=0}e=i.gzindexr&&(t.adler=j(t.adler,i.pending_buf,i.pending-r,r)),i.gzindex=0}i.status=91}if(91===i.status){if(i.gzhead.comment){let e,r=i.pending;do{if(i.pending===i.pending_buf_size){if(i.gzhead.hcrc&&i.pending>r&&(t.adler=j(t.adler,i.pending_buf,i.pending-r,r)),St(t),0!==i.pending)return i.last_flush=-1,it;r=0}e=i.gzindexr&&(t.adler=j(t.adler,i.pending_buf,i.pending-r,r))}i.status=103}if(103===i.status){if(i.gzhead.hcrc){if(i.pending+2>i.pending_buf_size&&(St(t),0!==i.pending))return i.last_flush=-1,it;zt(i,255&t.adler),zt(i,t.adler>>8&255),t.adler=0}if(i.status=_t,St(t),0!==i.pending)return i.last_flush=-1,it}if(0!==t.avail_in||0!==i.lookahead||e!==Q&&i.status!==yt){let r=0===i.level?It(i,e):i.strategy===ht?((t,e)=>{let i;for(;;){if(0===t.lookahead&&(Ft(t),0===t.lookahead)){if(e===Q)return 1;break}if(t.match_length=0,i=X(t,0,t.window[t.strstart]),t.lookahead--,t.strstart++,i&&(Mt(t,!1),0===t.strm.avail_out))return 1}return t.insert=0,e===tt?(Mt(t,!0),0===t.strm.avail_out?3:4):t.sym_next&&(Mt(t,!1),0===t.strm.avail_out)?1:2})(i,e):i.strategy===ct?((t,e)=>{let i,r,s,n;const a=t.window;for(;;){if(t.lookahead<=gt){if(Ft(t),t.lookahead<=gt&&e===Q)return 1;if(0===t.lookahead)break}if(t.match_length=0,t.lookahead>=3&&t.strstart>0&&(s=t.strstart-1,r=a[s],r===a[++s]&&r===a[++s]&&r===a[++s])){n=t.strstart+gt;do{}while(r===a[++s]&&r===a[++s]&&r===a[++s]&&r===a[++s]&&r===a[++s]&&r===a[++s]&&r===a[++s]&&r===a[++s]&&st.lookahead&&(t.match_length=t.lookahead)}if(t.match_length>=3?(i=X(t,1,t.match_length-3),t.lookahead-=t.match_length,t.strstart+=t.match_length,t.match_length=0):(i=X(t,0,t.window[t.strstart]),t.lookahead--,t.strstart++),i&&(Mt(t,!1),0===t.strm.avail_out))return 1}return t.insert=0,e===tt?(Mt(t,!0),0===t.strm.avail_out?3:4):t.sym_next&&(Mt(t,!1),0===t.strm.avail_out)?1:2})(i,e):Rt[i.level].func(i,e);if(3!==r&&4!==r||(i.status=yt),1===r||3===r)return 0===t.avail_out&&(i.last_flush=-1),it;if(2===r&&(e===$?K(i):e!==et&&(Y(i,0,0,!1),e===J&&(wt(i.head),0===i.lookahead&&(i.strstart=0,i.block_start=0,i.insert=0))),St(t),0===t.avail_out))return i.last_flush=-1,it}return e!==tt?it:i.wrap<=0?rt:(2===i.wrap?(zt(i,255&t.adler),zt(i,t.adler>>8&255),zt(i,t.adler>>16&255),zt(i,t.adler>>24&255),zt(i,255&t.total_in),zt(i,t.total_in>>8&255),zt(i,t.total_in>>16&255),zt(i,t.total_in>>24&255)):(Tt(i,t.adler>>>16),Tt(i,65535&t.adler)),St(t),i.wrap>0&&(i.wrap=-i.wrap),0!==i.pending?it:rt)},deflateEnd:t=>{if(Ut(t))return st;const e=t.state.status;return t.state=null,e===_t?bt(t,nt):it},deflateSetDictionary:(t,e)=>{let i=e.length;if(Ut(t))return st;const r=t.state,s=r.wrap;if(2===s||1===s&&r.status!==vt||r.lookahead)return st;if(1===s&&(t.adler=G(t.adler,e,i,0)),r.wrap=0,i>=r.w_size){0===s&&(wt(r.head),r.strstart=0,r.block_start=0,r.insert=0);let t=new Uint8Array(r.w_size);t.set(e.subarray(i-r.w_size,i),0),e=t,i=r.w_size}const n=t.avail_in,a=t.next_in,o=t.input;for(t.avail_in=i,t.next_in=0,t.input=e,Ft(r);r.lookahead>=3;){let t=r.strstart,e=r.lookahead-2;do{r.ins_h=Ct(r,r.ins_h,r.window[t+3-1]),r.prev[t&r.w_mask]=r.head[r.ins_h],r.head[r.ins_h]=t,t++}while(--e);r.strstart=t,r.lookahead=2,Ft(r)}return r.strstart+=r.lookahead,r.block_start=r.strstart,r.insert=r.lookahead,r.lookahead=0,r.match_length=r.prev_length=2,r.match_available=0,t.next_in=a,t.input=o,t.avail_in=n,r.wrap=s,it},deflateInfo:"pako deflate (from Nodeca project)"};const jt=(t,e)=>Object.prototype.hasOwnProperty.call(t,e);var Ht=function(t){const e=Array.prototype.slice.call(arguments,1);for(;e.length;){const i=e.shift();if(i){if("object"!=typeof i)throw new TypeError(i+"must be non-object");for(const e in i)jt(i,e)&&(t[e]=i[e])}}return t},Wt=t=>{let e=0;for(let i=0,r=t.length;i=252?6:t>=248?5:t>=240?4:t>=224?3:t>=192?2:1;Yt[254]=Yt[254]=1;var Zt=t=>{if("function"==typeof TextEncoder&&TextEncoder.prototype.encode)return(new TextEncoder).encode(t);let e,i,r,s,n,a=t.length,o=0;for(s=0;s>>6,e[n++]=128|63&i):i<65536?(e[n++]=224|i>>>12,e[n++]=128|i>>>6&63,e[n++]=128|63&i):(e[n++]=240|i>>>18,e[n++]=128|i>>>12&63,e[n++]=128|i>>>6&63,e[n++]=128|63&i);return e},Xt=(t,e)=>{const i=e||t.length;if("function"==typeof TextDecoder&&TextDecoder.prototype.decode)return(new TextDecoder).decode(t.subarray(0,e));let r,s;const n=new Array(2*i);for(s=0,r=0;r4)n[s++]=65533,r+=a-1;else{for(e&=2===a?31:3===a?15:7;a>1&&r1?n[s++]=65533:e<65536?n[s++]=e:(e-=65536,n[s++]=55296|e>>10&1023,n[s++]=56320|1023&e)}}return((t,e)=>{if(e<65534&&t.subarray&&qt)return String.fromCharCode.apply(null,t.length===e?t:t.subarray(0,e));let i="";for(let r=0;r{(e=e||t.length)>t.length&&(e=t.length);let i=e-1;for(;i>=0&&128==(192&t[i]);)i--;return i<0||0===i?e:i+Yt[t[i]]>e?i:e};var Qt=function(){this.input=null,this.next_in=0,this.avail_in=0,this.total_in=0,this.output=null,this.next_out=0,this.avail_out=0,this.total_out=0,this.msg="",this.state=null,this.data_type=2,this.adler=0};const $t=Object.prototype.toString,{Z_NO_FLUSH:Jt,Z_SYNC_FLUSH:te,Z_FULL_FLUSH:ee,Z_FINISH:ie,Z_OK:re,Z_STREAM_END:se,Z_DEFAULT_COMPRESSION:ne,Z_DEFAULT_STRATEGY:ae,Z_DEFLATED:oe}=W;function le(t){this.options=Ht({level:ne,method:oe,chunkSize:16384,windowBits:15,memLevel:8,strategy:ae},t||{});let e=this.options;e.raw&&e.windowBits>0?e.windowBits=-e.windowBits:e.gzip&&e.windowBits>0&&e.windowBits<16&&(e.windowBits+=16),this.err=0,this.msg="",this.ended=!1,this.chunks=[],this.strm=new Qt,this.strm.avail_out=0;let i=Vt.deflateInit2(this.strm,e.level,e.method,e.windowBits,e.memLevel,e.strategy);if(i!==re)throw new Error(H[i]);if(e.header&&Vt.deflateSetHeader(this.strm,e.header),e.dictionary){let t;if(t="string"==typeof e.dictionary?Zt(e.dictionary):"[object ArrayBuffer]"===$t.call(e.dictionary)?new Uint8Array(e.dictionary):e.dictionary,i=Vt.deflateSetDictionary(this.strm,t),i!==re)throw new Error(H[i]);this._dict_set=!0}}function he(t,e){const i=new le(e);if(i.push(t,!0),i.err)throw i.msg||H[i.err];return i.result}le.prototype.push=function(t,e){const i=this.strm,r=this.options.chunkSize;let s,n;if(this.ended)return!1;for(n=e===~~e?e:!0===e?ie:Jt,"string"==typeof t?i.input=Zt(t):"[object ArrayBuffer]"===$t.call(t)?i.input=new Uint8Array(t):i.input=t,i.next_in=0,i.avail_in=i.input.length;;)if(0===i.avail_out&&(i.output=new Uint8Array(r),i.next_out=0,i.avail_out=r),(n===te||n===ee)&&i.avail_out<=6)this.onData(i.output.subarray(0,i.next_out)),i.avail_out=0;else{if(s=Vt.deflate(i,n),s===se)return i.next_out>0&&this.onData(i.output.subarray(0,i.next_out)),s=Vt.deflateEnd(this.strm),this.onEnd(s),this.ended=!0,s===re;if(0!==i.avail_out){if(n>0&&i.next_out>0)this.onData(i.output.subarray(0,i.next_out)),i.avail_out=0;else if(0===i.avail_in)break}else this.onData(i.output)}return!0},le.prototype.onData=function(t){this.chunks.push(t)},le.prototype.onEnd=function(t){t===re&&(this.result=Wt(this.chunks)),this.chunks=[],this.err=t,this.msg=this.strm.msg};var ce={Deflate:le,deflate:he,deflateRaw:function(t,e){return(e=e||{}).raw=!0,he(t,e)},gzip:function(t,e){return(e=e||{}).gzip=!0,he(t,e)},constants:W};const de=16209;var ue=function(t,e){let i,r,s,n,a,o,l,h,c,d,u,f,p,g,m,v,_,y,b,x,w,A,C,S;const M=t.state;i=t.next_in,C=t.input,r=i+(t.avail_in-5),s=t.next_out,S=t.output,n=s-(e-t.avail_out),a=s+(t.avail_out-257),o=M.dmax,l=M.wsize,h=M.whave,c=M.wnext,d=M.window,u=M.hold,f=M.bits,p=M.lencode,g=M.distcode,m=(1<>>24,u>>>=y,f-=y,y=_>>>16&255,0===y)S[s++]=65535&_;else{if(!(16&y)){if(64&y){if(32&y){M.mode=16191;break t}t.msg="invalid literal/length code",M.mode=de;break t}_=p[(65535&_)+(u&(1<>>=y,f-=y),f<15&&(u+=C[i++]<>>24,u>>>=y,f-=y,y=_>>>16&255,16&y){if(x=65535&_,y&=15,fo){t.msg="invalid distance too far back",M.mode=de;break t}if(u>>>=y,f-=y,y=s-n,x>y){if(y=x-y,y>h&&M.sane){t.msg="invalid distance too far back",M.mode=de;break t}if(w=0,A=d,0===c){if(w+=l-y,y2;)S[s++]=A[w++],S[s++]=A[w++],S[s++]=A[w++],b-=3;b&&(S[s++]=A[w++],b>1&&(S[s++]=A[w++]))}else{w=s-x;do{S[s++]=S[w++],S[s++]=S[w++],S[s++]=S[w++],b-=3}while(b>2);b&&(S[s++]=S[w++],b>1&&(S[s++]=S[w++]))}break}if(64&y){t.msg="invalid distance code",M.mode=de;break t}_=g[(65535&_)+(u&(1<>3,i-=b,f-=b<<3,u&=(1<{const l=o.bits;let h,c,d,u,f,p,g=0,m=0,v=0,_=0,y=0,b=0,x=0,w=0,A=0,C=0,S=null;const M=new Uint16Array(16),z=new Uint16Array(16);let T,E,L,F=null;for(g=0;g<=fe;g++)M[g]=0;for(m=0;m=1&&0===M[_];_--);if(y>_&&(y=_),0===_)return s[n++]=20971520,s[n++]=20971520,o.bits=1,0;for(v=1;v<_&&0===M[v];v++);for(y0&&(0===t||1!==_))return-1;for(z[1]=0,g=1;g852||2===t&&A>592)return 1;for(;;){T=g-x,a[m]+1=p?(E=F[a[m]-p],L=S[a[m]-p]):(E=96,L=0),h=1<>x)+c]=T<<24|E<<16|L}while(0!==c);for(h=1<>=1;if(0!==h?(C&=h-1,C+=h):C=0,m++,0==--M[g]){if(g===_)break;g=e[i+a[m]]}if(g>y&&(C&u)!==d){for(0===x&&(x=y),f+=v,b=g-x,w=1<852||2===t&&A>592)return 1;d=C&u,s[d]=y<<24|b<<16|f-n}}return 0!==C&&(s[f+C]=g-x<<24|64<<16),o.bits=y,0};const{Z_FINISH:ye,Z_BLOCK:be,Z_TREES:xe,Z_OK:we,Z_STREAM_END:Ae,Z_NEED_DICT:Ce,Z_STREAM_ERROR:Se,Z_DATA_ERROR:Me,Z_MEM_ERROR:ze,Z_BUF_ERROR:Te,Z_DEFLATED:Ee}=W,Le=16180,Fe=16190,Ie=16191,Oe=16192,De=16194,ke=16199,Re=16200,Pe=16206,Ue=16209,Be=t=>(t>>>24&255)+(t>>>8&65280)+((65280&t)<<8)+((255&t)<<24);function Ne(){this.strm=null,this.mode=0,this.last=!1,this.wrap=0,this.havedict=!1,this.flags=0,this.dmax=0,this.check=0,this.total=0,this.head=null,this.wbits=0,this.wsize=0,this.whave=0,this.wnext=0,this.window=null,this.hold=0,this.bits=0,this.length=0,this.offset=0,this.extra=0,this.lencode=null,this.distcode=null,this.lenbits=0,this.distbits=0,this.ncode=0,this.nlen=0,this.ndist=0,this.have=0,this.next=null,this.lens=new Uint16Array(320),this.work=new Uint16Array(288),this.lendyn=null,this.distdyn=null,this.sane=0,this.back=0,this.was=0}const Ge=t=>{if(!t)return 1;const e=t.state;return!e||e.strm!==t||e.mode16211?1:0},Ve=t=>{if(Ge(t))return Se;const e=t.state;return t.total_in=t.total_out=e.total=0,t.msg="",e.wrap&&(t.adler=1&e.wrap),e.mode=Le,e.last=0,e.havedict=0,e.flags=-1,e.dmax=32768,e.head=null,e.hold=0,e.bits=0,e.lencode=e.lendyn=new Int32Array(852),e.distcode=e.distdyn=new Int32Array(592),e.sane=1,e.back=-1,we},je=t=>{if(Ge(t))return Se;const e=t.state;return e.wsize=0,e.whave=0,e.wnext=0,Ve(t)},He=(t,e)=>{let i;if(Ge(t))return Se;const r=t.state;return e<0?(i=0,e=-e):(i=5+(e>>4),e<48&&(e&=15)),e&&(e<8||e>15)?Se:(null!==r.window&&r.wbits!==e&&(r.window=null),r.wrap=i,r.wbits=e,je(t))},We=(t,e)=>{if(!t)return Se;const i=new Ne;t.state=i,i.strm=t,i.window=null,i.mode=Le;const r=He(t,e);return r!==we&&(t.state=null),r};let qe,Ye,Ze=!0;const Xe=t=>{if(Ze){qe=new Int32Array(512),Ye=new Int32Array(32);let e=0;for(;e<144;)t.lens[e++]=8;for(;e<256;)t.lens[e++]=9;for(;e<280;)t.lens[e++]=7;for(;e<288;)t.lens[e++]=8;for(_e(1,t.lens,0,288,qe,0,t.work,{bits:9}),e=0;e<32;)t.lens[e++]=5;_e(2,t.lens,0,32,Ye,0,t.work,{bits:5}),Ze=!1}t.lencode=qe,t.lenbits=9,t.distcode=Ye,t.distbits=5},Ke=(t,e,i,r)=>{let s;const n=t.state;return null===n.window&&(n.wsize=1<=n.wsize?(n.window.set(e.subarray(i-n.wsize,i),0),n.wnext=0,n.whave=n.wsize):(s=n.wsize-n.wnext,s>r&&(s=r),n.window.set(e.subarray(i-r,i-r+s),n.wnext),(r-=s)?(n.window.set(e.subarray(i-r,i),0),n.wnext=r,n.whave=n.wsize):(n.wnext+=s,n.wnext===n.wsize&&(n.wnext=0),n.whaveWe(t,15),inflateInit2:We,inflate:(t,e)=>{let i,r,s,n,a,o,l,h,c,d,u,f,p,g,m,v,_,y,b,x,w,A,C=0;const S=new Uint8Array(4);let M,z;const T=new Uint8Array([16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15]);if(Ge(t)||!t.output||!t.input&&0!==t.avail_in)return Se;i=t.state,i.mode===Ie&&(i.mode=Oe),a=t.next_out,s=t.output,l=t.avail_out,n=t.next_in,r=t.input,o=t.avail_in,h=i.hold,c=i.bits,d=o,u=l,A=we;t:for(;;)switch(i.mode){case Le:if(0===i.wrap){i.mode=Oe;break}for(;c<16;){if(0===o)break t;o--,h+=r[n++]<>>8&255,i.check=j(i.check,S,2,0),h=0,c=0,i.mode=16181;break}if(i.head&&(i.head.done=!1),!(1&i.wrap)||(((255&h)<<8)+(h>>8))%31){t.msg="incorrect header check",i.mode=Ue;break}if((15&h)!==Ee){t.msg="unknown compression method",i.mode=Ue;break}if(h>>>=4,c-=4,w=8+(15&h),0===i.wbits&&(i.wbits=w),w>15||w>i.wbits){t.msg="invalid window size",i.mode=Ue;break}i.dmax=1<>8&1),512&i.flags&&4&i.wrap&&(S[0]=255&h,S[1]=h>>>8&255,i.check=j(i.check,S,2,0)),h=0,c=0,i.mode=16182;case 16182:for(;c<32;){if(0===o)break t;o--,h+=r[n++]<>>8&255,S[2]=h>>>16&255,S[3]=h>>>24&255,i.check=j(i.check,S,4,0)),h=0,c=0,i.mode=16183;case 16183:for(;c<16;){if(0===o)break t;o--,h+=r[n++]<>8),512&i.flags&&4&i.wrap&&(S[0]=255&h,S[1]=h>>>8&255,i.check=j(i.check,S,2,0)),h=0,c=0,i.mode=16184;case 16184:if(1024&i.flags){for(;c<16;){if(0===o)break t;o--,h+=r[n++]<>>8&255,i.check=j(i.check,S,2,0)),h=0,c=0}else i.head&&(i.head.extra=null);i.mode=16185;case 16185:if(1024&i.flags&&(f=i.length,f>o&&(f=o),f&&(i.head&&(w=i.head.extra_len-i.length,i.head.extra||(i.head.extra=new Uint8Array(i.head.extra_len)),i.head.extra.set(r.subarray(n,n+f),w)),512&i.flags&&4&i.wrap&&(i.check=j(i.check,r,f,n)),o-=f,n+=f,i.length-=f),i.length))break t;i.length=0,i.mode=16186;case 16186:if(2048&i.flags){if(0===o)break t;f=0;do{w=r[n+f++],i.head&&w&&i.length<65536&&(i.head.name+=String.fromCharCode(w))}while(w&&f>9&1,i.head.done=!0),t.adler=i.check=0,i.mode=Ie;break;case 16189:for(;c<32;){if(0===o)break t;o--,h+=r[n++]<>>=7&c,c-=7&c,i.mode=Pe;break}for(;c<3;){if(0===o)break t;o--,h+=r[n++]<>>=1,c-=1,3&h){case 0:i.mode=16193;break;case 1:if(Xe(i),i.mode=ke,e===xe){h>>>=2,c-=2;break t}break;case 2:i.mode=16196;break;case 3:t.msg="invalid block type",i.mode=Ue}h>>>=2,c-=2;break;case 16193:for(h>>>=7&c,c-=7&c;c<32;){if(0===o)break t;o--,h+=r[n++]<>>16^65535)){t.msg="invalid stored block lengths",i.mode=Ue;break}if(i.length=65535&h,h=0,c=0,i.mode=De,e===xe)break t;case De:i.mode=16195;case 16195:if(f=i.length,f){if(f>o&&(f=o),f>l&&(f=l),0===f)break t;s.set(r.subarray(n,n+f),a),o-=f,n+=f,l-=f,a+=f,i.length-=f;break}i.mode=Ie;break;case 16196:for(;c<14;){if(0===o)break t;o--,h+=r[n++]<>>=5,c-=5,i.ndist=1+(31&h),h>>>=5,c-=5,i.ncode=4+(15&h),h>>>=4,c-=4,i.nlen>286||i.ndist>30){t.msg="too many length or distance symbols",i.mode=Ue;break}i.have=0,i.mode=16197;case 16197:for(;i.have>>=3,c-=3}for(;i.have<19;)i.lens[T[i.have++]]=0;if(i.lencode=i.lendyn,i.lenbits=7,M={bits:i.lenbits},A=_e(0,i.lens,0,19,i.lencode,0,i.work,M),i.lenbits=M.bits,A){t.msg="invalid code lengths set",i.mode=Ue;break}i.have=0,i.mode=16198;case 16198:for(;i.have>>24,v=C>>>16&255,_=65535&C,!(m<=c);){if(0===o)break t;o--,h+=r[n++]<>>=m,c-=m,i.lens[i.have++]=_;else{if(16===_){for(z=m+2;c>>=m,c-=m,0===i.have){t.msg="invalid bit length repeat",i.mode=Ue;break}w=i.lens[i.have-1],f=3+(3&h),h>>>=2,c-=2}else if(17===_){for(z=m+3;c>>=m,c-=m,w=0,f=3+(7&h),h>>>=3,c-=3}else{for(z=m+7;c>>=m,c-=m,w=0,f=11+(127&h),h>>>=7,c-=7}if(i.have+f>i.nlen+i.ndist){t.msg="invalid bit length repeat",i.mode=Ue;break}for(;f--;)i.lens[i.have++]=w}}if(i.mode===Ue)break;if(0===i.lens[256]){t.msg="invalid code -- missing end-of-block",i.mode=Ue;break}if(i.lenbits=9,M={bits:i.lenbits},A=_e(1,i.lens,0,i.nlen,i.lencode,0,i.work,M),i.lenbits=M.bits,A){t.msg="invalid literal/lengths set",i.mode=Ue;break}if(i.distbits=6,i.distcode=i.distdyn,M={bits:i.distbits},A=_e(2,i.lens,i.nlen,i.ndist,i.distcode,0,i.work,M),i.distbits=M.bits,A){t.msg="invalid distances set",i.mode=Ue;break}if(i.mode=ke,e===xe)break t;case ke:i.mode=Re;case Re:if(o>=6&&l>=258){t.next_out=a,t.avail_out=l,t.next_in=n,t.avail_in=o,i.hold=h,i.bits=c,ue(t,u),a=t.next_out,s=t.output,l=t.avail_out,n=t.next_in,r=t.input,o=t.avail_in,h=i.hold,c=i.bits,i.mode===Ie&&(i.back=-1);break}for(i.back=0;C=i.lencode[h&(1<>>24,v=C>>>16&255,_=65535&C,!(m<=c);){if(0===o)break t;o--,h+=r[n++]<>y)],m=C>>>24,v=C>>>16&255,_=65535&C,!(y+m<=c);){if(0===o)break t;o--,h+=r[n++]<>>=y,c-=y,i.back+=y}if(h>>>=m,c-=m,i.back+=m,i.length=_,0===v){i.mode=16205;break}if(32&v){i.back=-1,i.mode=Ie;break}if(64&v){t.msg="invalid literal/length code",i.mode=Ue;break}i.extra=15&v,i.mode=16201;case 16201:if(i.extra){for(z=i.extra;c>>=i.extra,c-=i.extra,i.back+=i.extra}i.was=i.length,i.mode=16202;case 16202:for(;C=i.distcode[h&(1<>>24,v=C>>>16&255,_=65535&C,!(m<=c);){if(0===o)break t;o--,h+=r[n++]<>y)],m=C>>>24,v=C>>>16&255,_=65535&C,!(y+m<=c);){if(0===o)break t;o--,h+=r[n++]<>>=y,c-=y,i.back+=y}if(h>>>=m,c-=m,i.back+=m,64&v){t.msg="invalid distance code",i.mode=Ue;break}i.offset=_,i.extra=15&v,i.mode=16203;case 16203:if(i.extra){for(z=i.extra;c>>=i.extra,c-=i.extra,i.back+=i.extra}if(i.offset>i.dmax){t.msg="invalid distance too far back",i.mode=Ue;break}i.mode=16204;case 16204:if(0===l)break t;if(f=u-l,i.offset>f){if(f=i.offset-f,f>i.whave&&i.sane){t.msg="invalid distance too far back",i.mode=Ue;break}f>i.wnext?(f-=i.wnext,p=i.wsize-f):p=i.wnext-f,f>i.length&&(f=i.length),g=i.window}else g=s,p=a-i.offset,f=i.length;f>l&&(f=l),l-=f,i.length-=f;do{s[a++]=g[p++]}while(--f);0===i.length&&(i.mode=Re);break;case 16205:if(0===l)break t;s[a++]=i.length,l--,i.mode=Re;break;case Pe:if(i.wrap){for(;c<32;){if(0===o)break t;o--,h|=r[n++]<{if(Ge(t))return Se;let e=t.state;return e.window&&(e.window=null),t.state=null,we},inflateGetHeader:(t,e)=>{if(Ge(t))return Se;const i=t.state;return 2&i.wrap?(i.head=e,e.done=!1,we):Se},inflateSetDictionary:(t,e)=>{const i=e.length;let r,s,n;return Ge(t)?Se:(r=t.state,0!==r.wrap&&r.mode!==Fe?Se:r.mode===Fe&&(s=1,s=G(s,e,i,0),s!==r.check)?Me:(n=Ke(t,e,i,i),n?(r.mode=16210,ze):(r.havedict=1,we)))},inflateInfo:"pako inflate (from Nodeca project)"};var $e=function(){this.text=0,this.time=0,this.xflags=0,this.os=0,this.extra=null,this.extra_len=0,this.name="",this.comment="",this.hcrc=0,this.done=!1};const Je=Object.prototype.toString,{Z_NO_FLUSH:ti,Z_FINISH:ei,Z_OK:ii,Z_STREAM_END:ri,Z_NEED_DICT:si,Z_STREAM_ERROR:ni,Z_DATA_ERROR:ai,Z_MEM_ERROR:oi}=W;function li(t){this.options=Ht({chunkSize:65536,windowBits:15,to:""},t||{});const e=this.options;e.raw&&e.windowBits>=0&&e.windowBits<16&&(e.windowBits=-e.windowBits,0===e.windowBits&&(e.windowBits=-15)),!(e.windowBits>=0&&e.windowBits<16)||t&&t.windowBits||(e.windowBits+=32),e.windowBits>15&&e.windowBits<48&&(15&e.windowBits||(e.windowBits|=15)),this.err=0,this.msg="",this.ended=!1,this.chunks=[],this.strm=new Qt,this.strm.avail_out=0;let i=Qe.inflateInit2(this.strm,e.windowBits);if(i!==ii)throw new Error(H[i]);if(this.header=new $e,Qe.inflateGetHeader(this.strm,this.header),e.dictionary&&("string"==typeof e.dictionary?e.dictionary=Zt(e.dictionary):"[object ArrayBuffer]"===Je.call(e.dictionary)&&(e.dictionary=new Uint8Array(e.dictionary)),e.raw&&(i=Qe.inflateSetDictionary(this.strm,e.dictionary),i!==ii)))throw new Error(H[i])}function hi(t,e){const i=new li(e);if(i.push(t),i.err)throw i.msg||H[i.err];return i.result}li.prototype.push=function(t,e){const i=this.strm,r=this.options.chunkSize,s=this.options.dictionary;let n,a,o;if(this.ended)return!1;for(a=e===~~e?e:!0===e?ei:ti,"[object ArrayBuffer]"===Je.call(t)?i.input=new Uint8Array(t):i.input=t,i.next_in=0,i.avail_in=i.input.length;;){for(0===i.avail_out&&(i.output=new Uint8Array(r),i.next_out=0,i.avail_out=r),n=Qe.inflate(i,a),n===si&&s&&(n=Qe.inflateSetDictionary(i,s),n===ii?n=Qe.inflate(i,a):n===ai&&(n=si));i.avail_in>0&&n===ri&&i.state.wrap>0&&0!==t[i.next_in];)Qe.inflateReset(i),n=Qe.inflate(i,a);switch(n){case ni:case ai:case si:case oi:return this.onEnd(n),this.ended=!0,!1}if(o=i.avail_out,i.next_out&&(0===i.avail_out||n===ri))if("string"===this.options.to){let t=Kt(i.output,i.next_out),e=i.next_out-t,s=Xt(i.output,t);i.next_out=e,i.avail_out=r-e,e&&i.output.set(i.output.subarray(t,t+e),0),this.onData(s)}else this.onData(i.output.length===i.next_out?i.output:i.output.subarray(0,i.next_out));if(n!==ii||0!==o){if(n===ri)return n=Qe.inflateEnd(this.strm),this.onEnd(n),this.ended=!0,!0;if(0===i.avail_in)break}}return!0},li.prototype.onData=function(t){this.chunks.push(t)},li.prototype.onEnd=function(t){t===ii&&("string"===this.options.to?this.result=this.chunks.join(""):this.result=Wt(this.chunks)),this.chunks=[],this.err=t,this.msg=this.strm.msg};var ci={Inflate:li,inflate:hi,inflateRaw:function(t,e){return(e=e||{}).raw=!0,hi(t,e)},ungzip:hi,constants:W};const{Deflate:di,deflate:ui,deflateRaw:fi,gzip:pi}=ce,{Inflate:gi,inflate:mi,inflateRaw:vi,ungzip:_i}=ci;var yi=di,bi=ui,xi=fi,wi=pi,Ai=gi,Ci=mi,Si=vi,Mi=_i,zi=W,Ti={Deflate:di,deflate:ui,deflateRaw:fi,gzip:pi,Inflate:gi,inflate:mi,inflateRaw:vi,ungzip:_i,constants:W}}},__webpack_module_cache__={};function __webpack_require__(t){var e=__webpack_module_cache__[t];if(void 0!==e)return e.exports;var i=__webpack_module_cache__[t]={exports:{}};return __webpack_modules__[t].call(i.exports,i,i.exports,__webpack_require__),i.exports}__webpack_require__.d=(t,e)=>{for(var i in e)__webpack_require__.o(e,i)&&!__webpack_require__.o(t,i)&&Object.defineProperty(t,i,{enumerable:!0,get:e[i]})},__webpack_require__.o=(t,e)=>Object.prototype.hasOwnProperty.call(t,e),__webpack_require__.r=t=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},__webpack_require__(421),__webpack_require__(185);var __webpack_exports__=__webpack_require__(307);return __webpack_exports__})())); \ No newline at end of file diff --git a/assets/variant_effect_inspection_tool/3Dmol-min.js.LICENSE.txt b/assets/variant_effect_inspection_tool/3Dmol-min.js.LICENSE.txt new file mode 100644 index 0000000..1b2a169 --- /dev/null +++ b/assets/variant_effect_inspection_tool/3Dmol-min.js.LICENSE.txt @@ -0,0 +1,90 @@ +3Dmol.js incorporates code from GLmol, Three.js, and jQuery and +is licensed under a BSD-3-Clause license. + +* 3Dmol.js + Copyright (c) 2014, University of Pittsburgh and contributors + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + 3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +* GLmol + GLmol - Molecular Viewer on WebGL/Javascript (0.47) + (C) Copyright 2011-2012, biochem_fan + License: dual license of MIT or LGPL3 + + Contributors: + Robert Hanson for parseXYZ, deferred instantiation + + + +* Three.js + https://github.com/mrdoob/three.js + + Copyright (c) 2010-2012 three.js Authors. All rights reserved. + + 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. + +* jQuery + http://jquery.org/ + + Copyright (c) 2011 John Resig + + 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/assets/variant_effect_inspection_tool/viewer_template.html b/assets/variant_effect_inspection_tool/viewer_template.html new file mode 100644 index 0000000..c1e4f5a --- /dev/null +++ b/assets/variant_effect_inspection_tool/viewer_template.html @@ -0,0 +1,907 @@ + + + + + + + nf-core/deepmutscan · variant effect inspection tool · __SAMPLE__ + + + +
+ + variant effect inspection tool + + +
+ +
+ + +
+ + +
+ +
+
+
+
+ + + + + diff --git a/conf/base.config b/conf/base.config index 3aae334..6dd4679 100644 --- a/conf/base.config +++ b/conf/base.config @@ -9,8 +9,6 @@ */ process { - - // TODO nf-core: Check the defaults for all processes cpus = { 1 * task.attempt } memory = { 6.GB * task.attempt } time = { 4.h * task.attempt } @@ -19,12 +17,17 @@ process { maxRetries = 1 maxErrors = '-1' + // Disable 'noclobber' and pre-clean versions.yml so modules can overwrite it + beforeScript = ''' + set +o noclobber + rm -f versions.yml || true + ''' + // Process-specific resource requirements // NOTE - Please try and reuse the labels below as much as possible. // These labels are used and recognised by default in DSL2 files hosted on nf-core/modules. // If possible, it would be nice to keep the same label naming convention when // adding in your local modules too. - // TODO nf-core: Customise requirements for specific processes. // See https://www.nextflow.io/docs/latest/config.html#config-process-selectors withLabel:process_single { cpus = { 1 } @@ -59,8 +62,4 @@ process { errorStrategy = 'retry' maxRetries = 2 } - withLabel: process_gpu { - ext.use_gpu = { workflow.profile.contains('gpu') } - accelerator = { workflow.profile.contains('gpu') ? 1 : null } - } } diff --git a/conf/containers_conda_lock_files_amd64.config b/conf/containers_conda_lock_files_amd64.config index d3ee1b4..41e234d 100644 --- a/conf/containers_conda_lock_files_amd64.config +++ b/conf/containers_conda_lock_files_amd64.config @@ -1,2 +1,2 @@ process { withName: 'FASTQC' { container = 'modules/nf-core/fastqc/.conda-lock/linux_amd64-bd-5cb1a2fa2f18c7c2_1.txt' } } -process { withName: 'MULTIQC' { container = 'modules/nf-core/multiqc/.conda-lock/linux_amd64-bd-db7c73dae76bc9e6_1.txt' } } +process { withName: 'MULTIQC' { container = 'modules/nf-core/multiqc/.conda-lock/linux_amd64-bd-c17fb751507e9dfc_1.txt' } } diff --git a/conf/containers_conda_lock_files_arm64.config b/conf/containers_conda_lock_files_arm64.config index 2b90ac4..5b5b9aa 100644 --- a/conf/containers_conda_lock_files_arm64.config +++ b/conf/containers_conda_lock_files_arm64.config @@ -1,2 +1,2 @@ process { withName: 'FASTQC' { container = 'modules/nf-core/fastqc/.conda-lock/linux_arm64-bd-e455e32f745abe68_1.txt' } } -process { withName: 'MULTIQC' { container = 'modules/nf-core/multiqc/.conda-lock/linux_arm64-bd-d167b8012595a136_1.txt' } } +process { withName: 'MULTIQC' { container = 'modules/nf-core/multiqc/.conda-lock/linux_arm64-bd-5c84a5000a226ab5_1.txt' } } diff --git a/conf/containers_docker_amd64.config b/conf/containers_docker_amd64.config index 65f1814..a66e3d1 100644 --- a/conf/containers_docker_amd64.config +++ b/conf/containers_docker_amd64.config @@ -1,2 +1,2 @@ process { withName: 'FASTQC' { container = 'community.wave.seqera.io/library/fastqc:0.12.1--5cb1a2fa2f18c7c2' } } -process { withName: 'MULTIQC' { container = 'community.wave.seqera.io/library/multiqc:1.34--db7c73dae76bc9e6' } } +process { withName: 'MULTIQC' { container = 'community.wave.seqera.io/library/multiqc:1.35--c17fb751507e9dfc' } } diff --git a/conf/containers_docker_arm64.config b/conf/containers_docker_arm64.config index 6c845ba..215f686 100644 --- a/conf/containers_docker_arm64.config +++ b/conf/containers_docker_arm64.config @@ -1,2 +1,2 @@ process { withName: 'FASTQC' { container = 'community.wave.seqera.io/library/fastqc:0.12.1--e455e32f745abe68' } } -process { withName: 'MULTIQC' { container = 'community.wave.seqera.io/library/multiqc:1.34--d167b8012595a136' } } +process { withName: 'MULTIQC' { container = 'community.wave.seqera.io/library/multiqc:1.35--5c84a5000a226ab5' } } diff --git a/conf/containers_singularity_https_amd64.config b/conf/containers_singularity_https_amd64.config index 838f248..2fb11a3 100644 --- a/conf/containers_singularity_https_amd64.config +++ b/conf/containers_singularity_https_amd64.config @@ -1,2 +1,2 @@ process { withName: 'FASTQC' { container = 'https://community-cr-prod.seqera.io/docker/registry/v2/blobs/sha256/f2/f20b021476d1d87658820f971ebecc1e8cdbde0f338eb0d9cea2b0a8fc54a54b/data' } } -process { withName: 'MULTIQC' { container = 'https://community-cr-prod.seqera.io/docker/registry/v2/blobs/sha256/1b/1bef8af6be88c5733461959c46ac8ef73d18f65277f62a1695d0e1633054f9c2/data' } } +process { withName: 'MULTIQC' { container = 'https://community-cr-prod.seqera.io/docker/registry/v2/blobs/sha256/c8/c8e346f4f6080eadf1253505e6ff09ef004454fc18e8d672006fd7b222cc412e/data' } } diff --git a/conf/containers_singularity_https_arm64.config b/conf/containers_singularity_https_arm64.config index 090173b..5fa5b4f 100644 --- a/conf/containers_singularity_https_arm64.config +++ b/conf/containers_singularity_https_arm64.config @@ -1,2 +1,2 @@ process { withName: 'FASTQC' { container = 'https://community-cr-prod.seqera.io/docker/registry/v2/blobs/sha256/46/46daf2dad0169afd2ae047c3e50ed3776259f664bf07e5e06b045dc23449e994/data' } } -process { withName: 'MULTIQC' { container = 'https://community-cr-prod.seqera.io/docker/registry/v2/blobs/sha256/9a/9a1fec9662a152683e6fcae440d0ce20920b3b89dc62d1e3a52e73f92eba0969/data' } } +process { withName: 'MULTIQC' { container = 'https://community-cr-prod.seqera.io/docker/registry/v2/blobs/sha256/e4/e48aa28aebc881254a499b24c3e1ce77b8df1b85a5432699ed6f72eb17ac7fb5/data' } } diff --git a/conf/containers_singularity_oras_amd64.config b/conf/containers_singularity_oras_amd64.config index 773f369..b334375 100644 --- a/conf/containers_singularity_oras_amd64.config +++ b/conf/containers_singularity_oras_amd64.config @@ -1,2 +1,2 @@ process { withName: 'FASTQC' { container = 'oras://community.wave.seqera.io/library/fastqc:0.12.1--5c4bd442468d75dd' } } -process { withName: 'MULTIQC' { container = 'oras://community.wave.seqera.io/library/multiqc:1.34--4fc8657c816047c0' } } +process { withName: 'MULTIQC' { container = 'oras://community.wave.seqera.io/library/multiqc:1.35--c680f2aea25ccec2' } } diff --git a/conf/containers_singularity_oras_arm64.config b/conf/containers_singularity_oras_arm64.config index 798cc63..661c656 100644 --- a/conf/containers_singularity_oras_arm64.config +++ b/conf/containers_singularity_oras_arm64.config @@ -1,2 +1,2 @@ process { withName: 'FASTQC' { container = 'oras://community.wave.seqera.io/library/fastqc:0.12.1--127a87fc06499035' } } -process { withName: 'MULTIQC' { container = 'oras://community.wave.seqera.io/library/multiqc:1.34--7fbd82d945c06726' } } +process { withName: 'MULTIQC' { container = 'oras://community.wave.seqera.io/library/multiqc:1.35--c0468833d65b2f81' } } diff --git a/conf/modules.config b/conf/modules.config index d203d2b..0dd3406 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -7,6 +7,9 @@ ext.args2 = Second set of arguments appended to command in module (multi-tool modules). ext.args3 = Third set of arguments appended to command in module (multi-tool modules). ext.prefix = File name prefix for output files. + + Output layout: Outputs are published directly into functional directories + under `${outdir}/...` without sample-specific or reference parent folders. ---------------------------------------------------------------------------------------- */ @@ -20,6 +23,12 @@ process { withName: FASTQC { ext.args = '--quiet' + containerOptions = '' + publishDir = [ + path: { "${params.outdir}/fastqc" }, + mode: params.publish_dir_mode, + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] } withName: 'MULTIQC' { @@ -31,4 +40,233 @@ process { ] } + // --- Reference-derived, merged into intermediate_files --- + + withName: 'BWA_INDEX' { + publishDir = [ + path: "${params.outdir}/intermediate_files", + mode: 'copy', + saveAs: { filename -> filename == 'versions.yml' ? null : filename } + ] + } + + withName: 'DMSANALYSIS_POSSIBLE_MUTATIONS' { + publishDir = [ + path: "${params.outdir}/intermediate_files", + mode: 'copy', + saveAs: { filename -> filename == 'versions.yml' ? null : filename } + ] + } + + withName: 'DMSANALYSIS_AASEQ' { + publishDir = [ + path: "${params.outdir}/intermediate_files", + mode: 'copy', + saveAs: { filename -> filename == 'versions.yml' ? null : filename } + ] + } + + // --- Alignment / filtering / merging --- + + withName: 'BWA_MEM' { + publishDir = [ + path: { "${params.outdir}/intermediate_files/bam_files/bwa/mem" }, + mode: 'copy', + saveAs: { filename -> filename == 'versions.yml' ? null : filename } + ] + } + + withName: 'BAMFILTER_DMS' { + publishDir = [ + path: { "${params.outdir}/intermediate_files/bam_files/filtered" }, + mode: 'copy', + saveAs: { filename -> filename == 'versions.yml' ? null : filename } + ] + } + + withName: 'PREMERGE' { + publishDir = [ + path: { "${params.outdir}/intermediate_files/bam_files/premerged" }, + mode: 'copy', + saveAs: { filename -> filename == 'versions.yml' ? null : filename } + ] + } + + withName: 'SAMTOOLS_SORT' { + ext.prefix = { "${meta.id}.sorted" } + publishDir = [ + path: { "${params.outdir}/intermediate_files/bam_files/sorted" }, + mode: 'copy', + saveAs: { filename -> filename == 'versions.yml' ? null : filename } + ] + } + + withName: 'SAMTOOLS_INDEX' { + publishDir = [ + path: { "${params.outdir}/intermediate_files/bam_files/sorted" }, + mode: 'copy', + saveAs: { filename -> filename == 'versions.yml' ? null : filename } + ] + } + + // --- Variant counting / processing / error correction (per-replicate meta.id subfolder) --- + + withName: 'VARIANTCOUNTING' { + publishDir = [ + path: { "${params.outdir}/intermediate_files/variant_counts" }, + mode: 'copy', + overwrite: false, + saveAs: { filename -> + if (filename == 'versions.yml') return null + "${meta.id}/${filename}" + } + ] + } + + withName: 'DMSANALYSIS_PROCESS_VARIANT_COUNTS' { + publishDir = [ + path: { "${params.outdir}/intermediate_files/processed_variant_counts" }, + mode: 'copy', + saveAs: { filename -> + if (filename == 'versions.yml') return null + "${meta.id}/${filename}" + } + ] + } + + withName: 'DMSANALYSIS_ERROR_CORRECTION_.*' { + publishDir = [ + path: { "${params.outdir}/intermediate_files/processed_variant_counts" }, + mode: 'copy', + saveAs: { filename -> + if (filename == 'versions.yml') return null + "${meta.id}/${filename}" + } + ] + } + + // Per-sample visualisations file under library_QC//. The negative lookahead excludes the two + // run-level reports, which have no per-sample `meta.id` to file under - Nextflow ACCUMULATES + // publishDir across matching selectors rather than letting a later `withName` override it, so if this + // selector also matched them its `${meta.id}` would still be evaluated (throwing for the EC report, + // and mis-filing the summary report under library_QC/run/). + withName: /.*VISUALIZATION_(?!ERROR_CORRECTION_REPORT|SUMMARY_REPORT).*/ { + publishDir = [ + path: { "${params.outdir}/library_QC" }, + mode: 'copy', + overwrite: false, + saveAs: { fn -> + if (fn == 'versions.yml') return null + "${meta.id}/${fn}" + } + ] + } + + // Run-level, so it has no meta.id to file itself under - published flat under library_QC. + withName: 'VISUALIZATION_ERROR_CORRECTION_REPORT' { + publishDir = [ + path: { "${params.outdir}/library_QC" }, + mode: 'copy', + saveAs: { filename -> filename == 'versions.yml' ? null : filename } + ] + } + + // The all-in-one report is the flagship output - published at the results root, not buried. + withName: 'VISUALIZATION_SUMMARY_REPORT' { + publishDir = [ + path: { "${params.outdir}" }, + mode: 'copy', + saveAs: { filename -> filename == 'versions.yml' ? null : filename } + ] + } + + // --- Fitness and Experimental Design --- + + withName: 'COUNTS_TO_FITNESS' { + publishDir = [ + path: { "${params.outdir}/fitness/DiMSum_results/single_rep_counts" }, + mode: 'copy', + saveAs: { filename -> filename == 'versions.yml' ? null : filename } + ] + } + + withName: 'MERGE_COUNTS' { + publishDir = [ + path: { "${params.outdir}/fitness" }, + mode: 'copy', + saveAs: { filename -> filename == 'versions.yml' ? null : filename } + ] + } + + withName: 'EXPDESIGN_FITNESS' { + publishDir = [ + path: "${params.outdir}/fitness", + mode: 'copy', + saveAs: { filename -> filename == 'versions.yml' ? null : filename } + ] + } + + withName: 'FIND_SYNONYMOUS_MUTATION' { + publishDir = [ + path: { "${params.outdir}/fitness" }, + mode: 'copy', + saveAs: { filename -> filename == 'versions.yml' ? null : filename } + ] + } + + withName: 'FITNESS_CALCULATION' { + publishDir = [ + path: { "${params.outdir}/fitness/default_results" }, + mode: 'copy', + saveAs: { filename -> filename == 'versions.yml' ? null : filename } + ] + } + + withName: 'FITNESS_QC' { + publishDir = [ + path: { "${params.outdir}/fitness/default_results" }, + mode: 'copy', + saveAs: { filename -> filename == 'versions.yml' ? null : filename } + ] + } + + withName: 'FITNESS_HEATMAP' { + publishDir = [ + path: { "${params.outdir}/fitness/default_results" }, + mode: 'copy', + saveAs: { filename -> filename == 'versions.yml' ? null : filename } + ] + } + + withName: 'RUN_DIMSUM' { + publishDir = [ + path: { "${params.outdir}/fitness/DiMSum_results" }, + mode: 'copy', + saveAs: { filename -> filename == 'versions.yml' ? null : filename } + ] + } + + withName: 'RUN_MUTSCAN' { + publishDir = [ + path: { "${params.outdir}/fitness/mutscan_results" }, + mode: 'copy', + saveAs: { filename -> filename == 'versions.yml' ? null : filename } + ] + } + + withName: 'VISUALIZATION_SUMMARY_REPORT' { + publishDir = [ + path: { "${params.outdir}" }, + mode: 'copy', + saveAs: { filename -> filename == 'versions.yml' ? null : filename } + ] + } + + withName: 'VARIANT_EFFECT_INSPECTION_TOOL' { + publishDir = [ + path: { "${params.outdir}/variant_effect_inspection_tool" }, + mode: 'copy', + saveAs: { filename -> filename == 'versions.yml' ? null : filename } + ] + } } diff --git a/conf/test.config b/conf/test.config index ae518f2..79a730b 100644 --- a/conf/test.config +++ b/conf/test.config @@ -13,7 +13,7 @@ process { resourceLimits = [ cpus: 4, - memory: '15.GB', + memory: '8.GB', time: '1.h' ] } @@ -23,8 +23,18 @@ params { config_profile_description = 'Minimal test dataset to check pipeline function' // Input data - // TODO nf-core: Specify the paths to your test data on nf-core/test-datasets - // TODO nf-core: Give any required params for the test so that command line flags are not needed - input = params.pipelines_testdata_base_path + 'viralrecon/samplesheet/samplesheet_test_illumina_amplicon.csv'// Genome references - genome = 'R64-1-1' + input = params.pipelines_testdata_base_path + 'deepmutscan/samplesheet/GID1A_test.csv' + fasta = params.pipelines_testdata_base_path + 'deepmutscan/testdata/GID1A.fasta' + reading_frame = '352-1383' + min_counts = 2 + mutagenesis_type = 'nnk_nns' + run_seqdepth = true + fitness = true + mutscan = true + dimsum = true + + // Precomputed wildtype structure (AlphaFold DB model, GID1A / UniProt Q9MAA7, CC-BY-4.0) so the + // interactive variant effect inspection tool runs end-to-end without structure prediction. + // TODO: move this fixture to nf-core/test-datasets and reference it via pipelines_testdata_base_path. + pdb = "${projectDir}/assets/test_structure/GID1A_AFDB.pdb" } diff --git a/conf/test_full.config b/conf/test_full.config index 27ac392..edb899d 100644 --- a/conf/test_full.config +++ b/conf/test_full.config @@ -17,7 +17,7 @@ params { // Input data for full size test // TODO nf-core: Specify the paths to your full test data ( on nf-core/test-datasets or directly in repositories, e.g. SRA) // TODO nf-core: Give any required params for the test so that command line flags are not needed - input = params.pipelines_testdata_base_path + 'viralrecon/samplesheet/samplesheet_full_illumina_amplicon.csv' + input = params.pipelines_testdata_base_path + 'samplesheet_qc_only.csv' // Genome references genome = 'R64-1-1' diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md index 3852535..639617e 100644 --- a/docs/CONTRIBUTING.md +++ b/docs/CONTRIBUTING.md @@ -179,7 +179,3 @@ nf-core pipelines bump-version --nextflow . #### Images and figures guidelines If you update images or graphics, follow the nf-core [style guidelines](https://nf-co.re/docs/community/brand/workflow-schematics). - -## Pipeline specific contribution guidelines - - diff --git a/docs/images/fastqc.png b/docs/images/fastqc.png new file mode 100644 index 0000000..32c30ff Binary files /dev/null and b/docs/images/fastqc.png differ diff --git a/docs/images/fitness_estimation_count_correlation.png b/docs/images/fitness_estimation_count_correlation.png new file mode 100644 index 0000000..98686bf Binary files /dev/null and b/docs/images/fitness_estimation_count_correlation.png differ diff --git a/docs/images/fitness_estimation_fitness_correlation.png b/docs/images/fitness_estimation_fitness_correlation.png new file mode 100644 index 0000000..81e30d8 Binary files /dev/null and b/docs/images/fitness_estimation_fitness_correlation.png differ diff --git a/docs/images/fitness_heatmap.png b/docs/images/fitness_heatmap.png new file mode 100644 index 0000000..a8e94e7 Binary files /dev/null and b/docs/images/fitness_heatmap.png differ diff --git a/docs/images/library_QC_SeqDepth.png b/docs/images/library_QC_SeqDepth.png new file mode 100644 index 0000000..bdd3ee8 Binary files /dev/null and b/docs/images/library_QC_SeqDepth.png differ diff --git a/docs/images/library_QC_counts_heatmap.png b/docs/images/library_QC_counts_heatmap.png new file mode 100644 index 0000000..850076e Binary files /dev/null and b/docs/images/library_QC_counts_heatmap.png differ diff --git a/docs/images/library_QC_counts_per_cov_heatmap.png b/docs/images/library_QC_counts_per_cov_heatmap.png new file mode 100644 index 0000000..03ad28f Binary files /dev/null and b/docs/images/library_QC_counts_per_cov_heatmap.png differ diff --git a/docs/images/library_QC_logdiff_plot.png b/docs/images/library_QC_logdiff_plot.png new file mode 100644 index 0000000..c04d900 Binary files /dev/null and b/docs/images/library_QC_logdiff_plot.png differ diff --git a/docs/images/library_QC_logdiff_varying_bases.png b/docs/images/library_QC_logdiff_varying_bases.png new file mode 100644 index 0000000..e26b9fb Binary files /dev/null and b/docs/images/library_QC_logdiff_varying_bases.png differ diff --git a/docs/images/library_QC_rolling_counts.png b/docs/images/library_QC_rolling_counts.png new file mode 100644 index 0000000..b7c71d5 Binary files /dev/null and b/docs/images/library_QC_rolling_counts.png differ diff --git a/docs/images/library_QC_rolling_coverage.png b/docs/images/library_QC_rolling_coverage.png new file mode 100644 index 0000000..b4ffe6b Binary files /dev/null and b/docs/images/library_QC_rolling_coverage.png differ diff --git a/docs/images/multiqc1.png b/docs/images/multiqc1.png new file mode 100644 index 0000000..a99a7b1 Binary files /dev/null and b/docs/images/multiqc1.png differ diff --git a/docs/images/multiqc2.png b/docs/images/multiqc2.png new file mode 100644 index 0000000..75ee227 Binary files /dev/null and b/docs/images/multiqc2.png differ diff --git a/docs/images/multiqc3.png b/docs/images/multiqc3.png new file mode 100644 index 0000000..d4c8c61 Binary files /dev/null and b/docs/images/multiqc3.png differ diff --git a/docs/images/nf-core-deepmutscan_logo_light.png b/docs/images/nf-core-deepmutscan_logo_light.png index f8bbe08..ed93fd9 100644 Binary files a/docs/images/nf-core-deepmutscan_logo_light.png and b/docs/images/nf-core-deepmutscan_logo_light.png differ diff --git a/docs/images/pipeline.png b/docs/images/pipeline.png new file mode 100644 index 0000000..fa66090 Binary files /dev/null and b/docs/images/pipeline.png differ diff --git a/docs/output.md b/docs/output.md index 743e91f..e316c7d 100644 --- a/docs/output.md +++ b/docs/output.md @@ -2,60 +2,139 @@ ## Introduction -This document describes the output produced by the pipeline. Most of the plots are taken from the MultiQC report, which summarises results at the end of the pipeline. +The directories listed below will be created in the results directory after `nf-core/deepmutscan` has finished. All paths are relative to the top-level results directory: + +Every sample-specific output is grouped under a per-sample parent folder (`/`, the `sample` column of the samplesheet). Files derived from the reference (shared across all samples) live in `reference/`, and genuinely global reports at the top level: + +```tree title="nf-core/deepmutscan results" +results/ +├── / # everything belonging to one biological sample +│ ├── fastqc/ # raw sequencing QC reports for this sample's fastq files +│ ├── intermediate_files/ # alignments, raw / pre-filtered / error-corrected variant count tables +│ ├── library_QC/ # per-replicate QC visualisations + the error-correction report +│ ├── fitness/ # merged counts, fitness + error estimates, heatmaps, DiMSum / mutscan +│ └── variant_effect_inspection_tool/ # interactive HTML tool (only with --pdb) +├── reference/ # reference-derived, shared: BWA index, aa_seq.txt, possible_mutations.csv +├── experimental_design/ # run-level DiMSum experimental design (from the whole samplesheet) +├── multiqc/ # shared raw sequencing QC report for all fastq files +├── pipeline_info/ # Nextflow helper files, timeline and summary report +├── timeline.html +└── report.html +``` -The directories listed below will be created in the results directory after the pipeline has finished. All paths are relative to the top-level results directory. +### FastQC + +[FastQC](http://www.bioinformatics.babraham.ac.uk/projects/fastqc/) gives general quality metrics about your sequenced reads. It provides information about the quality score distribution across your reads, per base sequence content (%A/T/G/C), adapter contamination and overrepresented sequences. For further reading and documentation see the [FastQC help pages](http://www.bioinformatics.babraham.ac.uk/projects/fastqc/Help/). + +
+Output files - +- `fastqc/` + - `*_fastqc.zip`: Zip archive containing the FastQC report, tab-delimited data file and plot images + - `*_fastqc.html`: FastQC report containing quality metrics -## Pipeline overview +![FASTQC report](images/fastqc.png) -The pipeline is built using [Nextflow](https://www.nextflow.io/) and processes data using the following steps: +
-- [FastQC](#fastqc) - Raw read QC -- [MultiQC](#multiqc) - Aggregate report describing results and QC from the whole pipeline -- [Pipeline information](#pipeline-information) - Report metrics generated during the workflow execution +### MultiQC -### FastQC +[MultiQC](http://multiqc.info) is a visualization tool that generates a single HTML report summarising all samples in your project. Most of the pipeline QC results are visualised in the report and further statistics are available in the report data directory. Results generated by MultiQC collate pipeline QC from supported tools e.g. FastQC. The pipeline has special steps which also allow the software versions to be reported in the MultiQC output for future traceability. For more information about how to use MultiQC reports, see .### Pipeline information
Output files -- `fastqc/` - - `*_fastqc.html`: FastQC report containing quality metrics. - - `*_fastqc.zip`: Zip archive containing the FastQC report, tab-delimited data file and plot images. +- `multiqc/` + - `multiqc_report.html`: a standalone `.html` file that can be viewed in your web browser + - `multiqc_data/`: directory containing parsed statistics from the different tools used in the pipeline + - `multiqc_plots/`: directory containing static images from the report in various formats + +![MULTIQC overview](images/multiqc1.png) +![MULTIQC base-quality summary](images/multiqc2.png) +![MULTIQC GC-content summary](images/multiqc3.png)
-[FastQC](http://www.bioinformatics.babraham.ac.uk/projects/fastqc/) gives general quality metrics about your sequenced reads. It provides information about the quality score distribution across your reads, per base sequence content (%A/T/G/C), adapter contamination and overrepresented sequences. For further reading and documentation see the [FastQC help pages](http://www.bioinformatics.babraham.ac.uk/projects/fastqc/Help/). +### Intermediate files (Core Pipeline Stages 1-4) -### MultiQC +This directory is created during the first series of steps of the pipeline, featuring raw read alignments, filtering and variant counting.
Output files -- `multiqc/` - - `multiqc_report.html`: a standalone HTML file that can be viewed in your web browser. - - `multiqc_data/`: directory containing parsed statistics from the different tools used in the pipeline. - - `multiqc_plots/`: directory containing static images from the report in various formats. +- `intermediate_files/` + - `aa_seq.txt`: a string of the reconstructed wildtype amino acid sequence of the specified open reading frame + - `possible_mutations.csv`: using the `--mutagenesis` argument, this file lists all the programmed mutations per position; these are used for variant count filtering and visualisation + - `bam_files/bwa/`: sets of BWA referenes indices from the original alignment(s), with BAM files for each sample in the `mem/` subfolder + - `bam_files/filtered/`: filtered BAM files for each sample, without indel-matching reads (wildtype reads are retained for downstream sequencing-error correction) + - `bam_files/premerged/`: filtered, read-merged and re-aligned BAM files for each sample, representing highest-quality alignments for subsequent variant counting + - `bam_files/sorted/`: coordinate-sorted and indexed premerged BAM files (required by the variant counter) + - `variant_counts/`: subfolders with the resulting variant count table (`*.variant_counts.tsv`) plus a `variant_counts_columns.tsv` column-description sheet, stratified by sample + - `processed_variant_counts/`: subfolders with prefiltered variant count tables, stratified by sample
-[MultiQC](http://multiqc.info) is a visualization tool that generates a single HTML report summarising all samples in your project. Most of the pipeline QC results are visualised in the report and further statistics are available in the report data directory. +### Library QC (Core Pipeline Stage 5) + +This directory is created during the second series of steps of the pipeline, featuring various QC visualisations for each sample. + +
+Output files + +- `library_QC/` + - `counts_heatmap.pdf`: a complete heatmap of absolute mutant counts, stratified by mutant amino acid (Y-axis) per position (X-axis) + ![Count heatmap](images/library_QC_counts_heatmap.png) + + - `counts_per_cov_heatmap.pdf`: as above, but as a fraction of the total sequencing coverage + - `logdiff_plot.pdf`: sorted, log-scale coverage distribution of all mutants + ![Logarithmic count differences](images/library_QC_logdiff_plot.png) + + - `logdiff_varying_bases.pdf`: as above, but stratified by hamming distance to the wildtype nucleotide sequence (colour shading) + - `rolling_coverage.pdf`: sliding-window rolling coverage + ![Rolling coverage](images/library_QC_rolling_coverage.png) + + - `rolling_counts.pdf`: sliding-window rolling coverage, stratified by hamming distance to the wildtype nucleotide sequence (colour shading) + ![Rolling counts](images/library_QC_rolling_counts.png) -Results generated by MultiQC collate pipeline QC from supported tools e.g. FastQC. The pipeline has special steps which also allow the software versions to be reported in the MultiQC output for future traceability. For more information about how to use MultiQC reports, see . + - `rolling_counts_per_cov.pdf`: as above, but as a fraction of the total sequencing coverage + - `SeqDepth.pdf` (optional via the `--run_seqdepth` argument): rarefaction curve of the sequencing coverage and how it relates to the percentage of programmed variants detected + ![Sequencing coverage rarefaction](images/library_QC_SeqDepth.png) -### Pipeline information + - `/_error_correction_report.html` (unless `--error_correction none`): a self-contained interactive report showing the effect of the sequencing-error correction — positional error bias along the ORF, the distribution of per-variant correction magnitude, a raw-vs-corrected scatter, and a searchable per-variant table. + +
+ +### Fitness (Core Pipeline Stages 6-8) + +This directory is created during the final series of steps of the pipeline, featuring fitness and fitness error estimates (when DMS input/output sample groups are specified).
Output files +- `fitness/` + - `counts_merged.tsv`: summarised gene variant counts across all input and output samples + - `default_results/fitness_estimation_count_correlation.pdf`: pair-wise replicate variant count scatterplots and correlations between all specified samples + ![Variant count correlation(s)](images/fitness_estimation_count_correlation.png) + + - `default_results/fitness_estimation_fitness_correlation.pdf`: pair-wise fitness replicate scatterplots and correlations between all specified output samples + ![Fitness correlation(s)](images/fitness_estimation_fitness_correlation.png) + + - `default_results/fitness_heatmap.pdf`: a complete heatmap of absolute mutant counts, stratified by mutant amino acid (Y-axis) per position (X-axis) + ![Default fitness heatmap](images/fitness_heatmap.png) + + - `default_results/fitness_estimation.tsv`: table file with all fitness and fitness error estimates calculated + - `DiMSum_results/dimsum_results/` (optional): subfolder with the full set of [DiMSum](https://github.com/lehner-lab/DiMSum) outputs, including the associated `.HTML` report, `.Rdata` and `.tsv` files with fitness and fitness error estimates + +- `/variant_effect_inspection_tool.html` (only with `--pdb`): a single, portable, interactive HTML tool that projects per-residue metrics (fitness, coverage, counts, counts/coverage, and — when correction is on — positional error bias) onto the supplied wildtype 3D structure. Select a residue to inspect each substitution's effect; download high-resolution images/PyMOL sessions. Works offline and can be shared as a single file. + +
+ +### Pipeline Info (Nextflow Reports) + - `pipeline_info/` - Reports generated by Nextflow: `execution_report.html`, `execution_timeline.html`, `execution_trace.txt` and `pipeline_dag.dot`/`pipeline_dag.svg`. - Reports generated by the pipeline: `pipeline_report.html`, `pipeline_report.txt` and `software_versions.yml`. The `pipeline_report*` files will only be present if the `--email` / `--email_on_fail` parameter's are used when running the pipeline. - Reformatted samplesheet files used as input to the pipeline: `samplesheet.valid.csv`. - Parameters used by the pipeline run: `params.json`. - - [Nextflow](https://www.nextflow.io/docs/latest/tracing.html) provides excellent functionality for generating various reports relevant to the running and execution of the pipeline. This will allow you to troubleshoot errors with the running of the pipeline, and also provide you with other information such as launch commands, run times and resource usage. diff --git a/docs/pipeline_steps.md b/docs/pipeline_steps.md new file mode 100644 index 0000000..5e6e2ed --- /dev/null +++ b/docs/pipeline_steps.md @@ -0,0 +1,114 @@ +# nf-core/deepmutscan: Detailed Pipeline Steps + +This page provides in-depth descriptions of the data processing modules implemented in **nf-core/deepmutscan**. It is mainly intended for advanced users and developers who want to understand the rationale behind design choices, explore implementation details, and consider potential future extensions. + +--- + +## Overview + +The pipeline processes deep mutational scanning (DMS) sequencing data in several stages: + +1. Alignment of reads to the reference open reading frame (ORF) +2. Filtering of wildtype and erroneous reads +3. Read merging for base error reduction +4. Mutation counting +5. DMS library quality control +6. Data summarisation across samples +7. Single nucleotide variant error correction +8. Fitness estimation _(in development)_ + +![pipeline](/docs/pipeline.png) + +Each step is explained below. Links are provided to the primary tools and libraries used, where applicable. + +--- + +## 1. Alignment + +All paired-end raw reads are first aligned to the provided reference ORF using [**bwa-mem**](http://bio-bwa.sourceforge.net/). This is a highly efficient mapping algorithm for reads ≥100 bp, with its multi-threading support automatically handled by nf-core. + +In future versions of nf-core/deepmutscan, we consider the use of [**bwa-mem2**](https://github.com/bwa-mem2/bwa-mem2), which provides similar alignment rates with a moderate speed increase ([Vasimuddin et al., _IPDPS_ 2019](https://ieeexplore.ieee.org/document/8820962)). With the increasing diversity of sequencing platforms for DMS, new throughput, read length, and error profiles may require further alignment options to be implemented. + +--- + +## 2. Filtering + +For long ORF site-saturation mutagenesis libraries, most aligned shotgun sequencing reads contain exact matches against the reference. Erroneous reads with unexpected indels are removed, while exact-match (wildtype) reads are retained — they are ignored by the variant counter and kept available for downstream sequencing-error correction. + +To this end, we use [**samtools view**](https://www.htslib.org/doc/samtools.html). + +--- + +## 3. Read Merging + +Even the highest-accuracy next-generation sequencing platforms do not have perfect base accuracy. To minimise the effect of base errors (which would otherwise be counted as "false mutations"), nf-core/deepmutscan uses the overlap of each aligned read pair. With base errors on the forward and reverse read being independent, the pipeline applies the [**vsearch fastq_mergepairs**](https://github.com/torognes/vsearch) function to convert each read pair into a single consensus molecule with adjusted base error scores. + +> [!TIP] +> Optimal merging performance is usually obtained if the average DNA fragment size matches the read size. For example, libraries sequenced with 150 bp paired-end reads should ideally also be sheared/tagmented to a mean size of 150 bp. + +Future versions may offer additional options depending on sequencing type and error profiles. + +--- + +## 4. Variant Counting + +Aligned consensus reads are screened for exact, base-level mismatches. nf-core/deepmutscan uses a lightweight, dependency-free Python counter (built on [**pysam**](https://pysam.readthedocs.io) and [**polars**](https://pola.rs)) to count occurrences of all single, double, triple, and higher-order nucleotide changes between each read and the reference ORF, from a coordinate-sorted, indexed BAM. The counter replaces GATK `AnalyzeSaturationMutagenesis` (keeping the container light) while producing a column-compatible count table. + +Two options tune the counter: `--base_qual` sets the minimum base quality for a mutation to be counted (default Q30; not available in GATK), and `--min_flank` sets the minimum distance (nt) a mutation or covered base must lie from a read edge (default 2, matching GATK's `--min-flanking-length`; 0 disables). The `--min_flank` window is applied to both variant calls and coverage. + +--- + +## 5. DMS Library Quality Control + +By integrating the reference ORF coordinates and the chosen DMS library type (default: NNK/NNs degenerate codon-based nicking), nf-core/deepmutscan calculates a number of mutation count summary statistics. + +Custom visualisations allow for inspection of (1) mutation efficiency along the ORF, (2) position-specific recovery of amino acid diversity, and (3) overall sequencing coverage evenness and saturation. + +--- + +## 6. Data Summarisation for Fitness Estimation + +Steps 1-5 are iteratively run across all samples defined in the `.csv` spreadsheet. Once read alignment, merging, mutation counting, and library QC have been completed for the full list of samples, users can opt to proceed with fitness estimation. To this end, the pipeline generates all the necessary input files by merging mutation counts across samples. + +--- + +## 7. Single Nucleotide Variant Error Correction + +With very deep sequencing, single-codon counts show position-dependent bias from sequencing errors. `nf-core/deepmutscan` distinguishes true single-nucleotide variants from artefacts via the `--error_correction` parameter, with three options: + +- `false_doubles` (**default**): because the library only contains single-codon changes, any observed multi-codon variant ("false double") must arise from sequencing error. Their counts, with a distance-dependent coverage model, estimate the per-nucleotide error rate that is subtracted from the single-codon counts. Needs no extra data. `--false_doubles_method` picks the estimator: `mle` (default, per-variant maximum likelihood) or `eb` (empirical Bayes, pooled and shrunk per substitution class — steadier for sparse variants). `--false_doubles_codon_window` (default 40) sets the codon matching window. +- `none`: no correction. +- `wildtype`: empirical error-rate modelling from **additional deep wildtype-only sequencing**. Add the wildtype sample(s) to the samplesheet with `type: wildtype`; their position-specific error profile is subtracted from each library sample of the same `sample`. This replaces the false-doubles correction. + +The corrected counts feed the fitness estimation and the mutation-count heatmaps; the corrected count tables are written next to the uncorrected ones under `intermediate_files/processed_variant_counts/`. + +--- + +## 8. Fitness Estimation _(in development)_ + +The final step of the pipeline will perform fitness estimation based on mutation counts. By default, we calculate fitness scores as the logarithm of variants' output to input ratio, normalised to that of the provided wildtype sequence. Future expansions may include: + +- Integration of other popular fitness inference tools, including [DiMSum](https://github.com/lehner-lab/DiMSum), [Enrich2](https://github.com/FowlerLab/Enrich2), [rosace](https://github.com/pimentellab/rosace/) and [mutscan](https://github.com/fmicompbio/mutscan) +- Standardised output formats for downstream analyses and comparison + +> [!IMPORTANT] +> We note that exact wildtype sequence reads are filtered out in stage 2. Including synonymous wildtype codons in the original mutagenesis design is therefore essential when it comes to calibrating the fitness calculations. + +--- + +## Notes for Developers + +- Custom scripts used in filtering and mutation counting are available in the `bin/` directory of the repository. +- Modules are implemented in Nextflow DSL2 and follow the nf-core community guidelines. +- Contributions, optimisations, and additional analysis modules are welcome - please open a pull request or GitHub issue to discuss ideas. + +_This document is meant as a living reference. As the pipeline evolves, the descriptions of steps 7 and 8 will be expanded with concrete implementation details._ + +--- + +## Contact + +For detailled scientific or technical questions, feedback and experimental discussions, feel free to contact us directly: + +- Benjamin Wehnert — wehnertbenjamin@gmail.com +- Maximilian Stammnitz — maximilian.stammnitz@crg.eu diff --git a/docs/usage.md b/docs/usage.md index 8edeeee..97fd15f 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -6,69 +6,33 @@ ## Introduction - +**nf-core/deepmutscan** is a workflow designed for the analysis of deep mutational scanning (DMS) data. DMS enables researchers to experimentally measure the fitness effects of thousands of genes or gene variants simultaneously, helping to classify disease causing mutants in human and animal populations, to learn the fundamental rules of virus evolution, protein architecture, splicing, small-molecule interactions and many other phenotypes. -## Samplesheet input - -You will need to create a samplesheet with information about the samples you would like to analyse before running the pipeline. Use this parameter to specify its location. It has to be a comma-separated file with 3 columns, and a header row as shown in the examples below. - -```bash ---input '[path to samplesheet file]' -``` - -### Multiple runs of the same sample - -The `sample` identifiers have to be the same when you have re-sequenced the same sample more than once e.g. to increase sequencing depth. The pipeline will concatenate the raw reads before performing any downstream analysis. Below is an example for the same sample sequenced across 3 lanes: - -```csv title="samplesheet.csv" -sample,fastq_1,fastq_2 -CONTROL_REP1,AEG588A1_S1_L002_R1_001.fastq.gz,AEG588A1_S1_L002_R2_001.fastq.gz -CONTROL_REP1,AEG588A1_S1_L003_R1_001.fastq.gz,AEG588A1_S1_L003_R2_001.fastq.gz -CONTROL_REP1,AEG588A1_S1_L004_R1_001.fastq.gz,AEG588A1_S1_L004_R2_001.fastq.gz -``` - -### Full samplesheet - -The pipeline will auto-detect whether a sample is single- or paired-end using the information provided in the samplesheet. The samplesheet can have as many columns as you desire, however, there is a strict requirement for the first 3 columns to match those defined in the table below. - -A final samplesheet file consisting of both single- and paired-end data may look something like the one below. This is for 6 samples, where `TREATMENT_REP3` has been sequenced twice. - -```csv title="samplesheet.csv" -sample,fastq_1,fastq_2 -CONTROL_REP1,AEG588A1_S1_L002_R1_001.fastq.gz,AEG588A1_S1_L002_R2_001.fastq.gz -CONTROL_REP2,AEG588A2_S2_L002_R1_001.fastq.gz,AEG588A2_S2_L002_R2_001.fastq.gz -CONTROL_REP3,AEG588A3_S3_L002_R1_001.fastq.gz,AEG588A3_S3_L002_R2_001.fastq.gz -TREATMENT_REP1,AEG588A4_S4_L003_R1_001.fastq.gz, -TREATMENT_REP2,AEG588A5_S5_L003_R1_001.fastq.gz, -TREATMENT_REP3,AEG588A6_S6_L003_R1_001.fastq.gz, -TREATMENT_REP3,AEG588A6_S6_L004_R1_001.fastq.gz, -``` - -| Column | Description | -| --------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `sample` | Custom sample name. This entry will be identical for multiple sequencing libraries/runs from the same sample. Spaces in sample names are automatically converted to underscores (`_`). | -| `fastq_1` | Full path to FastQ file for Illumina short reads 1. File has to be gzipped and have the extension ".fastq.gz" or ".fq.gz". | -| `fastq_2` | Full path to FastQ file for Illumina short reads 2. File has to be gzipped and have the extension ".fastq.gz" or ".fq.gz". | - -An [example samplesheet](../assets/samplesheet.csv) has been provided with the pipeline. +This page provides in-depth descriptions of the data processing modules implemented in `nf-core/deepmutscan`. It is similarly intended for new deep mutational scanning aficionados, for advanced users and developers who want to understand the rationale behind certain design choices, to explore implementation details and consider potential future extensions. ## Running the pipeline -The typical command for running the pipeline is as follows: +The typical command for running the pipeline (on an example protein-coding gene with 100 amino acids) is as follows: -```bash -nextflow run nf-core/deepmutscan --input ./samplesheet.csv --outdir ./results --genome GRCh37 -profile docker +```bash title="example_run.sh" +nextflow run nf-core/deepmutscan \ + -profile \ + --input ./samplesheet.csv \ + --fasta ./ref.fa \ + --reading_frame 1-300 \ + --outdir ./results ``` -This will launch the pipeline with the `docker` configuration profile. See below for more information about profiles. +The `-profile ` specification is mandatory and should reflect either your own institutional profile or any pipeline profile specified in the [profile section](##-profile). + +This will launch the pipeline by performing sequencing read alignments, various raw data QC analyses, optional mutant count error corrections, fitness and fitness error estimations. Note that the pipeline will create the following files in your working directory: -```bash -work # Directory containing the nextflow working files - # Finished results in specified location (defined with --outdir) -.nextflow_log # Log file from Nextflow -# Other nextflow hidden files, eg. history of pipeline runs and old logs. +```console title="working directory" +work # Directory containing the nextflow working files +results # Finished results in specified location (defined with --outdir); needs full writing access +.nextflow_log # Log file from Nextflow ``` If you wish to repeatedly use the same parameters for multiple runs, rather than specifying each flag in the command, you can specify these in a params file. @@ -80,7 +44,7 @@ Pipeline settings can be provided in a `yaml` or `json` file via `-params-file < The above pipeline run specified with a params file in yaml format: -```bash +```bash title="example_run.sh" nextflow run nf-core/deepmutscan -profile docker -params-file params.yaml ``` @@ -89,32 +53,153 @@ with: ```yaml title="params.yaml" input: './samplesheet.csv' outdir: './results/' -genome: 'GRCh37' +gene reference: 'ref.fa' <...> ``` You can also generate such `YAML`/`JSON` files via [nf-core/launch](https://nf-co.re/launch). -### Updating the pipeline +## Inputs + +Users need to first prepare a samplesheet with your input/output data in which each row represents a pair of matched fastq files (paired end). This should look as follows: + +```csv title="samplesheet.csv" +sample,type,replicate,file1,file2 +ORF1,input,1,/reads/forward1.fastq.gz,/reads/reverse1.fastq.gz +ORF1,input,2,/reads/forward2.fastq.gz,/reads/reverse2.fastq.gz +ORF1,output,1,/reads/forward3.fastq.gz,/reads/reverse3.fastq.gz +ORF1,output,2,/reads/forward4.fastq.gz,/reads/reverse4.fastq.gz +``` + +Secondly, users need to specify the gene or gene region of interest using a reference FASTA file via `--fasta`. Provide the exact codon coordinates using `--reading_frame`. + +## Optional parameters + +Several optional parameters are available for `nf-core/deepmutscan`, some of which are currently _(in development)_. + +| Parameter | Default | Description | +| -------------------- | --------------- | ------------------------------------------------------------------------------ | +| `--run_seqdepth` | `true` | Estimate sequencing-depth saturation by closed-form hypergeometric rarefaction | +| `--fitness` | `false` | Default fitness inference module | +| `--dimsum` | `false` | Optional fitness inference module _(AMD/x86_64 systems only)_ | +| `--mutagenesis` | `nnk` | Deep mutational scanning strategy used | +| `--error-estimation` | `wt_sequencing` | Error model used to correct 1nt counts _(in development)_ | +| `--read-align` | `bwa-mem` | Customised read aligner _(in development)_ | + +## Pipeline output + +After execution, the pipeline creates the following directory structure: + +```tree title="nf-core/deepmutscan results" +results/ +├── fastqc/ # Individual HTML reports for specified fastq files, raw sequencing QC +├── fitness/ # Merged variant count tables, fitness and error estimates, replicate correlations and heatmaps +├── intermediate_files/ # Raw alignments, raw and pre-filtered variant count tables, QC reports +├── library_QC/ # Sample-specific PDF visualizations: position-wise sequencing coverage, count heatmaps, etc. +├── multiqc/ # Shared HTML reports for all fastq files, raw sequencing QC +├── pipelineinfo/ # Nextflow helper files for timeline and summary report generation +├── timeline.html # Nextflow timeline for all tasks +└── report.html # Nextflow summary report incl. detailed CPU and memory usage per for all tasks +``` + +## Detailed steps + +### 1. Alignment + +All paired-end raw reads are first aligned to the provided reference ORF using [**bwa-mem**](http://bio-bwa.sourceforge.net/). This is a highly efficient mapping algorithm for reads ≥100 bp, with its multi-threading support automatically handled by nf-core. + +In future versions of `nf-core/deepmutscan`, we consider the use of [**bwa-mem2**](https://github.com/bwa-mem2/bwa-mem2), which provides similar alignment quality at moderate speed increase ([Vasimuddin et al., _IPDPS_ 2019](https://ieeexplore.ieee.org/document/8820962)). With the increasing diversity of sequencing platforms for DMS new read length, throughput and error profiles may require further alignment options to be implemented. + +### 2. Filtering + +For long ORF site-saturation mutagenesis libraries, most aligned shotgun sequencing reads contain exact matches against the reference. Reads with likely artefactual indel-containing alignments are removed, while exact-match (wildtype) reads are retained — they are ignored by the variant counter and kept available for downstream sequencing-error correction. + +To this end, we use [**samtools view**](https://www.htslib.org/doc/samtools.html). + +### 3. Read Merging + +Even the highest-quality next-generation sequencing platforms do not feature perfect base accuracy. To minimise the effect of base errors (which would otherwise be counted as "false mutations"), `nf-core/deepmutscan` uses the overlap of each aligned read pair. With base errors on the forward and reverse read being independent, the pipeline applies the [**vsearch fastq_mergepairs**](https://github.com/torognes/vsearch) function to convert each read pair into a single consensus molecule with adjusted base error scores. + +> [!TIP] +> Optimal merging benefit is usually obtained if the average DNA fragment size matches the read size. For example, libraries sequenced with 150 bp paired-end reads should ideally also be sheared/tagmented to a mean size of 150 bp. + +Future versions may offer additional options depending on sequencing type and error profiles. + +### 4. Variant Counting + +Aligned consensus reads are screened for exact, base-level mismatches. `nf-core/deepmutscan` uses a lightweight, dependency-free Python counter (built on [**pysam**](https://pysam.readthedocs.io) and [**polars**](https://pola.rs)) to count occurrences of all single, double, triple, and higher-order nucleotide changes between each read and the reference ORF, from a coordinate-sorted, indexed BAM. It produces a variant count table that is column-compatible with the previously used GATK `AnalyzeSaturationMutagenesis` output. + +Two options tune the counter: `--base_qual` sets the minimum base quality for a mutation to be counted (default `30`; not available in GATK), and `--min_flank` sets the minimum distance (in nt) a mutation or covered base must be from a read edge (default `2`, matching GATK's `--min-flanking-length`; set to `0` to disable). The `--min_flank` window is applied consistently to both variant calls and the coverage calculation. + +### 5. DMS Library Quality Control + +By integrating the reference ORF coordinates and the chosen DMS library type (default: NNK degenerate codons), `nf-core/deepmutscan` calculates a number of mutation count summary statistics. + +Custom visualisations allow for inspection of (1) mutation efficiency along the ORF, (2) position-specific recovery of mutant amino acid diversity, and (3) overall sequencing coverage evenness and library saturation. + +### 6. Data Summarisation for Fitness Estimation + +Steps 1-5 are run in parallel across all individual samples defined in the `.csv` spreadsheet. Once read alignment, filtering, merging, variant counting, and DMS library QC have been completed for the full list of samples – if input/output sample pairs are available – users can opt to proceed towards fitness estimation. To this end, the pipeline generates all the necessary preparatory files by generating a merged mutation count table across samples. + +### 7. Single Nucleotide Variant Error Correction + +Very deep sequencing introduces position-dependent false counts. Select a strategy with `--error_correction`: + +- `false_doubles` (**default**): the library only contains single-codon changes, so observed multi-codon variants ("false doubles") are sequencing errors. Their counts, together with a distance-dependent coverage model, estimate the per-nucleotide error rate, which is subtracted from the single-codon counts. No extra data required. Two estimators are available via `--false_doubles_method`: + - `mle` (**default**): a per-variant maximum-likelihood error rate. + - `eb`: an empirical-Bayes estimate that pools across variants and shrinks per substitution class, which is steadier for sparsely observed variants. + + The matching window (in codons) is set by `--false_doubles_codon_window` (default 40). + +- `none`: disables error correction (counts pass through unchanged). +- `wildtype`: subtracts a position-specific error profile measured from an **additional deep wildtype-only sequencing** sample. Provide it in the samplesheet with `type: wildtype`, sharing the same `sample` name as the input/output samples it should correct. This replaces the false-doubles correction. + +The corrected counts are used for every count-dependent step (fitness, count heatmaps, positional-bias and coverage QC). Whenever correction is applied, a self-contained `*_error_correction_report.html` is written per sample so its effect can be inspected interactively. + +### Interactive variant effect inspection tool (`--pdb`) + +When `--fitness` is set and a wildtype 3D structure is supplied via `--pdb ` (a crystal structure, an AlphaFold DB model, or any PDB matching the wildtype ORF), the pipeline additionally builds a single, portable, interactive HTML tool per sample. It projects per-residue fitness, coverage, counts, counts/coverage and — when error correction is on — the positional error bias onto the structure; clicking a residue shows each substitution's effect. Structure prediction (nf-core/proteinfold) is not wired in this release, so `--pdb` is required to enable the tool. + +### 8. Fitness Estimation + +The final step of the pipeline will perform fitness estimation based on mutation counts. By default, we calculate fitness scores as the logarithm of variants' output to input ratio, normalised to that of the provided wildtype nucleotide sequence. + +Future expansions may include: + +- Integration of other popular fitness inference tools, including [DiMSum](https://github.com/lehner-lab/DiMSum), [Enrich2](https://github.com/FowlerLab/Enrich2), [rosace](https://github.com/pimentellab/rosace/) and [mutscan](https://github.com/fmicompbio/mutscan) +- Standardised output formats for downstream analyses and comparison + +> [!IMPORTANT] +> We note that exact wildtype sequence reads are filtered out in stage 2. Including synonymous wildtype codons in the original mutagenesis design is therefore essential when it comes to calibrating the fitness calculations. + +## Notes for Developers + +- Custom R and Python scripts used in variant counting, filtering, error correction and QC visualisation live in each module's own `templates/` directory (e.g. `modules/local/dmsanalysis/*/templates/`). +- Modules are implemented in Nextflow DSL2 and follow the nf-core community guidelines. +- Contributions, optimisations, and additional analysis modules are welcome: please open a Github [issue](https://github.com/nf-core/deepmutscan/issues/new) or [pull request](https://github.com/nf-core/deepmutscan/compare) to discuss or suggest ideas. + +_This document is meant as a living reference. As the pipeline evolves, the descriptions of steps 7 and 8 will be further expanded with concrete implementation details._ + +## Updating the pipeline -When you run the above command, Nextflow automatically pulls the pipeline code from GitHub and stores it as a cached version. When running the pipeline after this, it will always use the cached version if available - even if the pipeline has been updated since. To make sure that you're running the latest version of the pipeline, make sure that you regularly update the cached version of the pipeline: +When you run the original command above, Nextflow automatically pulls the pipeline code from GitHub and stores it as a cached version. When running the pipeline after this, it will always use this cached version if available - even if the pipeline has been updated since. To make sure that you are running the latest version of the pipeline, make sure that you regularly update the cached version: ```bash nextflow pull nf-core/deepmutscan ``` -### Reproducibility +## Reproducibility It is a good idea to specify the pipeline version when running the pipeline on your data. This ensures that a specific version of the pipeline code and software are used when you run your pipeline. If you keep using the same tag, you'll be running the same version of the pipeline, even if there have been changes to the code since. -First, go to the [nf-core/deepmutscan releases page](https://github.com/nf-core/deepmutscan/releases) and find the latest pipeline version - numeric only (eg. `1.3.1`). Then specify this when running the pipeline with `-r` (one hyphen) - eg. `-r 1.3.1`. Of course, you can switch to another version by changing the number after the `-r` flag. +First, go to the [nf-core/deepmutscan releases page](https://github.com/nf-core/deepmutscan/releases) and find the latest pipeline version - numeric only (eg. `1.0.0`). Then specify this when running the pipeline with `-r` (one hyphen) - eg. `-r 1.0.0`. This version number will be logged in reports when you run the pipeline, so that you'll know what you used when you look back in the future. For example, at the bottom of the MultiQC reports. To further assist in reproducibility, you can use share and reuse [parameter files](#running-the-pipeline) to repeat pipeline runs with the same settings without having to write out a command with every single parameter. > [!TIP] -> If you wish to share such profile (such as upload as supplementary material for academic publications), make sure to NOT include cluster specific paths to files, nor institutional specific profiles. +> If you wish to share such a profile (e.g. providing it as supplementary material for academic publications), make sure to _not_ include your cluster specific file paths or institutional specific profiles. ## Core Nextflow arguments @@ -135,7 +220,7 @@ The pipeline also dynamically loads configurations from [https://github.com/nf-c Note that multiple profiles can be loaded, for example: `-profile test,docker` - the order of arguments is important! They are loaded in sequence, so later profiles can overwrite earlier profiles. -If `-profile` is not specified, the pipeline will run locally and expect all software to be installed and available on the `PATH`. This is _not_ recommended, since it can lead to different results on different machines dependent on the computer environment. +If `-profile` is not specified, the pipeline will run locally and expect all software to be installed and available on the `PATH`. This is _not_ recommended, since it may lead to varying or even irreproducible results across users' different computer environments. - `test` - A profile with a complete configuration for automated testing @@ -149,7 +234,7 @@ If `-profile` is not specified, the pipeline will run locally and expect all sof - `shifter` - A generic configuration profile to be used with [Shifter](https://nersc.gitlab.io/development/shifter/how-to-use/) - `charliecloud` - - A generic configuration profile to be used with [Charliecloud](https://charliecloud.io/) + - A generic configuration profile to be used with [Charliecloud](https://hpc.github.io/charliecloud/) - `apptainer` - A generic configuration profile to be used with [Apptainer](https://apptainer.org/) - `wave` @@ -159,7 +244,7 @@ If `-profile` is not specified, the pipeline will run locally and expect all sof ### `-resume` -Specify this when restarting a pipeline. Nextflow will use cached results from any pipeline steps where the inputs are the same, continuing from where it got to previously. For input to be considered the same, not only the names must be identical but the files' contents as well. For more info about this parameter, see [this blog post](https://www.nextflow.io/blog/2019/demystifying-nextflow-resume.html). +Specify this when restarting a pipeline. Nextflow will use cached results (from within the `/work` directory) from any pipeline steps where the inputs are the same, continuing from where it got to previously. For input to be considered the same, not only the names must be identical but the files' contents as well. For more info about this parameter, see [this blog post](https://www.nextflow.io/blog/2019/demystifying-nextflow-resume.html). You can also supply a run name to resume a specific run: `-resume [run-name]`. Use the `nextflow log` command to show previous run names. diff --git a/log.txt b/log.txt new file mode 100644 index 0000000..dceb2c1 --- /dev/null +++ b/log.txt @@ -0,0 +1,613 @@ +May-07 23:01:49.322 [main] DEBUG nextflow.cli.Launcher - $> nextflow run . -profile test,docker --fasta /Users/benjaminwehnert/GID1A_SUNi_ref_small.fasta --reading_frame 352-1383 --min_counts 2 --mutagenesis_type max_diff_to_wt --outdir ./results +May-07 23:01:49.500 [main] DEBUG nextflow.cli.CmdRun - N E X T F L O W ~ version 24.04.4 +May-07 23:01:49.524 [main] DEBUG nextflow.plugin.PluginsFacade - Setting up plugin manager > mode=prod; embedded=false; plugins-dir=/Users/benjaminwehnert/.nextflow/plugins; core-plugins: nf-amazon@2.5.3,nf-azure@1.6.1,nf-cloudcache@0.4.1,nf-codecommit@0.2.1,nf-console@1.1.3,nf-ga4gh@1.3.0,nf-google@1.13.2-patch1,nf-tower@1.9.1,nf-wave@1.4.2-patch1 +May-07 23:01:49.533 [main] INFO o.pf4j.DefaultPluginStatusProvider - Enabled plugins: [] +May-07 23:01:49.534 [main] INFO o.pf4j.DefaultPluginStatusProvider - Disabled plugins: [] +May-07 23:01:49.536 [main] INFO org.pf4j.DefaultPluginManager - PF4J version 3.12.0 in 'deployment' mode +May-07 23:01:49.549 [main] INFO org.pf4j.AbstractPluginManager - No plugins +May-07 23:01:50.385 [main] WARN nextflow.config.Manifest - Invalid config manifest attribute `contributors` +May-07 23:01:50.406 [main] DEBUG nextflow.config.ConfigBuilder - Found config local: /Users/benjaminwehnert/dmscore/nextflow.config +May-07 23:01:50.408 [main] DEBUG nextflow.config.ConfigBuilder - Parsing config file: /Users/benjaminwehnert/dmscore/nextflow.config +May-07 23:01:50.426 [main] DEBUG n.secret.LocalSecretsProvider - Secrets store: /Users/benjaminwehnert/.nextflow/secrets/store.json +May-07 23:01:50.428 [main] DEBUG nextflow.secret.SecretsLoader - Discovered secrets providers: [nextflow.secret.LocalSecretsProvider@169268a7] - activable => nextflow.secret.LocalSecretsProvider@169268a7 +May-07 23:01:50.438 [main] DEBUG nextflow.config.ConfigBuilder - Applying config profile: `test,docker` +May-07 23:01:51.909 [main] DEBUG nextflow.config.ConfigBuilder - Available config profiles: [bih, cfc_dev, uzl_omics, ifb_core, embl_hd, denbi_qbic, alice, mjolnir_globe, uppmax, giga, incliva, ilifu, ki_luria, uge, icr_alma, rosalind_uge, lugh, mccleary, unibe_ibu, vai, czbiohub_aws, jax, roslin, ccga_med, tes, scw, unc_longleaf, tigem, tubingen_apg, google, apollo, ipop_up, vsc_calcua, pdc_kth, googlels, ceci_nic5, humantechnopole, stjude, daisybio, eddie, medair, biowulf, apptainer, bi, bigpurple, adcra, cedars, pawsey_setonix, vsc_kul_uhasselt, pawsey_nimbus, ucl_myriad, utd_ganymede, charliecloud, seattlechildrens, icr_davros, ceres, arm, munin, rosalind, hasta, cfc, uzh, shu_bmrc, ebi_codon_slurm, ebc, ccga_dx, crick, ku_sund_danhead, marvin, shifter, biohpc_gen, mana, mamba, york_viking, unc_lccc, wehi, awsbatch, wustl_htcf, arcc, ceci_dragon2, imperial, maestro, software_license, cannon, genotoul, nci_gadi, abims, janelia, nu_genomics, googlebatch, oist, sahmri, kaust, alliance_canada, mpcdf, leicester, vsc_ugent, create, sage, cambridge, jex, podman, ebi_codon, cheaha, xanadu, nyu_hpc, test, marjorie, computerome, ucd_sonic, seg_globe, mssm, sanger, dkfz, bluebear, pasteur, einstein, ethz_euler, m3c, test_full, imb, ucl_cscluster, tuos_stanage, azurebatch, hki, seadragon, crukmi, csiro_petrichor, qmul_apocrita, wave, docker, engaging, gis, hypatia, psmn, eva, unity, cropdiversityhpc, nygc, fgcz, conda, crg, singularity, mpcdf_viper, pe2, self_hosted_runner, tufts, uw_hyak_pedslabs, binac2, debug, genouest, cbe, unsw_katana, gitpod, phoenix, seawulf, uod_hpc, fub_curta, uct_hpc, aws_tower, binac, fsu_draco] +May-07 23:01:51.958 [main] DEBUG nextflow.cli.CmdRun - Applied DSL=2 by global default +May-07 23:01:51.972 [main] DEBUG nextflow.cli.CmdRun - Launching `./main.nf` [modest_coulomb] DSL2 - revision: 84101fc51c +May-07 23:01:51.974 [main] DEBUG nextflow.plugin.PluginsFacade - Plugins declared=[nf-schema@2.3.0] +May-07 23:01:51.974 [main] DEBUG nextflow.plugin.PluginsFacade - Plugins default=[] +May-07 23:01:51.974 [main] DEBUG nextflow.plugin.PluginsFacade - Plugins resolved requirement=[nf-schema@2.3.0] +May-07 23:01:51.975 [main] DEBUG nextflow.plugin.PluginUpdater - Installing plugin nf-schema version: 2.3.0 +May-07 23:01:51.983 [main] INFO org.pf4j.AbstractPluginManager - Plugin 'nf-schema@2.3.0' resolved +May-07 23:01:51.983 [main] INFO org.pf4j.AbstractPluginManager - Start plugin 'nf-schema@2.3.0' +May-07 23:01:51.990 [main] DEBUG nextflow.plugin.BasePlugin - Plugin started nf-schema@2.3.0 +May-07 23:01:52.045 [main] DEBUG nextflow.Session - Session UUID: 2397b75e-3882-46d7-ba2c-8549f8a2b4a6 +May-07 23:01:52.045 [main] DEBUG nextflow.Session - Run name: modest_coulomb +May-07 23:01:52.046 [main] DEBUG nextflow.Session - Executor pool size: 8 +May-07 23:01:52.053 [main] DEBUG nextflow.file.FilePorter - File porter settings maxRetries=3; maxTransfers=50; pollTimeout=null +May-07 23:01:52.057 [main] DEBUG nextflow.util.ThreadPoolBuilder - Creating thread pool 'FileTransfer' minSize=10; maxSize=24; workQueue=LinkedBlockingQueue[10000]; allowCoreThreadTimeout=false +May-07 23:01:52.077 [main] DEBUG nextflow.cli.CmdRun - + Version: 24.04.4 build 5917 + Created: 01-08-2024 07:05 UTC (09:05 CEST) + System: Mac OS X 15.0 + Runtime: Groovy 4.0.21 on OpenJDK 64-Bit Server VM 17.0.13+0 + Encoding: UTF-8 (UTF-8) + Process: 31358@MacBook-Air-von-Benjamin.local [127.0.0.1] + CPUs: 8 - Mem: 8 GB (94.6 MB) - Swap: 8 GB (737.5 MB) +May-07 23:01:52.088 [main] DEBUG nextflow.Session - Work-dir: /Users/benjaminwehnert/dmscore/work [Mac OS X] +May-07 23:01:52.088 [main] DEBUG nextflow.Session - Script base path does not exist or is not a directory: /Users/benjaminwehnert/dmscore/bin +May-07 23:01:52.104 [main] DEBUG nextflow.executor.ExecutorFactory - Extension executors providers=[] +May-07 23:01:52.123 [main] DEBUG nextflow.Session - Observer factory: DefaultObserverFactory +May-07 23:01:52.143 [main] DEBUG nextflow.Session - Observer factory: ValidationObserverFactory +May-07 23:01:52.174 [main] WARN nextflow.config.Manifest - Invalid config manifest attribute `contributors` +May-07 23:01:52.195 [main] DEBUG nextflow.cache.CacheFactory - Using Nextflow cache factory: nextflow.cache.DefaultCacheFactory +May-07 23:01:52.205 [main] DEBUG nextflow.util.CustomThreadPool - Creating default thread pool > poolSize: 9; maxThreads: 1000 +May-07 23:01:52.264 [main] DEBUG nextflow.Session - Session start +May-07 23:01:52.266 [main] DEBUG nextflow.trace.TraceFileObserver - Workflow started -- trace file: /Users/benjaminwehnert/dmscore/results/pipeline_info/execution_trace_2025-05-07_23-01-50.txt +May-07 23:01:52.411 [main] DEBUG nextflow.script.ScriptRunner - > Launching execution +May-07 23:01:53.516 [main] DEBUG nextflow.script.IncludeDef - Loading included plugin extensions with names: [paramsSummaryMap:paramsSummaryMap]; plugin Id: nf-schema +May-07 23:01:53.917 [main] DEBUG nextflow.script.IncludeDef - Loading included plugin extensions with names: [paramsSummaryLog:paramsSummaryLog]; plugin Id: nf-schema +May-07 23:01:53.918 [main] DEBUG nextflow.script.IncludeDef - Loading included plugin extensions with names: [validateParameters:validateParameters]; plugin Id: nf-schema +May-07 23:01:53.920 [main] DEBUG nextflow.script.IncludeDef - Loading included plugin extensions with names: [paramsSummaryMap:paramsSummaryMap]; plugin Id: nf-schema +May-07 23:01:53.921 [main] DEBUG nextflow.script.IncludeDef - Loading included plugin extensions with names: [samplesheetToList:samplesheetToList]; plugin Id: nf-schema +May-07 23:01:54.131 [main] WARN nextflow.script.ScriptBinding - Access to undefined parameter `custom_codon_library` -- Initialise it to a default value eg. `params.custom_codon_library = some_value` +May-07 23:01:54.356 [main] DEBUG nextflow.script.IncludeDef - Loading included plugin extensions with names: [paramsSummaryLog:paramsSummaryLog]; plugin Id: nf-schema +May-07 23:01:54.357 [main] DEBUG nextflow.script.IncludeDef - Loading included plugin extensions with names: [validateParameters:validateParameters]; plugin Id: nf-schema +May-07 23:01:54.359 [main] DEBUG nextflow.script.IncludeDef - Loading included plugin extensions with names: [paramsSummaryMap:paramsSummaryMap]; plugin Id: nf-schema +May-07 23:01:54.360 [main] DEBUG nextflow.script.IncludeDef - Loading included plugin extensions with names: [samplesheetToList:samplesheetToList]; plugin Id: nf-schema +May-07 23:01:54.574 [main] INFO nextflow.Nextflow - +------------------------------------------------------ + ,--./,-. + ___ __ __ __ ___ /,-._.--~' + |\ | |__ __ / ` / \ |__) |__ } { + | \| | \__, \__/ | \ |___ \`-._,-`-, + `._,._,' + nf-core/dmscore 1.0.0dev +------------------------------------------------------ +Input/output options + input : https://raw.githubusercontent.com/BenjaminWehnert1008/test-datasets/dmsqc/dmsqc/samplesheet_qc_only.csv + outdir : ./results + min_counts : 2 + mutagenesis_type : max_diff_to_wt + +Reference genome options + genome : R64-1-1 + fasta : /Users/benjaminwehnert/GID1A_SUNi_ref_small.fasta + +Institutional config options + config_profile_name : Test profile + config_profile_description: Minimal test dataset to check pipeline function + +Generic options + trace_report_suffix : 2025-05-07_23-01-50 + +Core Nextflow options + runName : modest_coulomb + containerEngine : docker + launchDir : /Users/benjaminwehnert/dmscore + workDir : /Users/benjaminwehnert/dmscore/work + projectDir : /Users/benjaminwehnert/dmscore + userName : benjaminwehnert + profile : test,docker + configFiles : /Users/benjaminwehnert/dmscore/nextflow.config + +!! Only displaying parameters that differ from the pipeline defaults !! +------------------------------------------------------ +* The nf-core framework + https://doi.org/10.1038/s41587-020-0439-x + +* Software dependencies + https://github.com/nf-core/dmscore/blob/master/CITATIONS.md + +May-07 23:01:54.576 [main] DEBUG n.validation.ValidationExtension - Starting parameters validation +May-07 23:01:54.920 [main] DEBUG nextflow.validation.SchemaEvaluator - Started validating /BenjaminWehnert1008/test-datasets/dmsqc/dmsqc/samplesheet_qc_only.csv +May-07 23:01:55.945 [main] DEBUG nextflow.validation.SchemaEvaluator - Validation of file 'https://raw.githubusercontent.com/BenjaminWehnert1008/test-datasets/dmsqc/dmsqc/samplesheet_qc_only.csv' passed! +May-07 23:01:56.095 [main] DEBUG n.v.FormatDirectoryPathEvaluator - Cloud blob storage paths are not supported by 'FormatDirectoryPathEvaluator': 's3://ngi-igenomes/igenomes/' +May-07 23:01:56.099 [main] DEBUG n.validation.ValidationExtension - Finishing parameters validation +May-07 23:01:56.178 [main] DEBUG nextflow.script.ProcessConfig - Config settings `withLabel:process_medium` matches labels `process_medium` for process with name NFCORE_DMSCORE:DMSCORE:FASTQC +May-07 23:01:56.182 [main] DEBUG nextflow.script.ProcessConfig - Config settings `withName:FASTQC` matches process NFCORE_DMSCORE:DMSCORE:FASTQC +May-07 23:01:56.196 [main] DEBUG nextflow.executor.ExecutorFactory - << taskConfig executor: null +May-07 23:01:56.196 [main] DEBUG nextflow.executor.ExecutorFactory - >> processorType: 'local' +May-07 23:01:56.202 [main] DEBUG nextflow.executor.Executor - [warm up] executor > local +May-07 23:01:56.206 [main] DEBUG n.processor.LocalPollingMonitor - Creating local task monitor for executor 'local' > cpus=8; memory=8 GB; capacity=8; pollInterval=100ms; dumpInterval=5m +May-07 23:01:56.209 [main] DEBUG n.processor.TaskPollingMonitor - >>> barrier register (monitor: local) +May-07 23:01:56.359 [main] DEBUG nextflow.script.ProcessConfig - Config settings `withLabel:process_single` matches labels `process_single` for process with name NFCORE_DMSCORE:DMSCORE:MULTIQC +May-07 23:01:56.360 [main] DEBUG nextflow.script.ProcessConfig - Config settings `withName:MULTIQC` matches process NFCORE_DMSCORE:DMSCORE:MULTIQC +May-07 23:01:56.362 [main] DEBUG nextflow.executor.ExecutorFactory - << taskConfig executor: null +May-07 23:01:56.363 [main] DEBUG nextflow.executor.ExecutorFactory - >> processorType: 'local' +May-07 23:01:56.372 [main] DEBUG nextflow.script.ProcessConfig - Config settings `withLabel:process_single` matches labels `process_single` for process with name NFCORE_DMSCORE:DMSCORE:BWA_INDEX +May-07 23:01:56.373 [main] DEBUG nextflow.script.ProcessConfig - Config settings `withName:BWA_INDEX` matches process NFCORE_DMSCORE:DMSCORE:BWA_INDEX +May-07 23:01:56.375 [main] DEBUG nextflow.executor.ExecutorFactory - << taskConfig executor: null +May-07 23:01:56.375 [main] DEBUG nextflow.executor.ExecutorFactory - >> processorType: 'local' +May-07 23:01:56.391 [main] DEBUG nextflow.script.ProcessConfig - Config settings `withLabel:process_high` matches labels `process_high` for process with name NFCORE_DMSCORE:DMSCORE:BWA_MEM +May-07 23:01:56.391 [main] DEBUG nextflow.script.ProcessConfig - Config settings `withName:BWA_MEM` matches process NFCORE_DMSCORE:DMSCORE:BWA_MEM +May-07 23:01:56.394 [main] DEBUG nextflow.executor.ExecutorFactory - << taskConfig executor: null +May-07 23:01:56.395 [main] DEBUG nextflow.executor.ExecutorFactory - >> processorType: 'local' +May-07 23:01:56.404 [main] DEBUG nextflow.script.ProcessConfig - Config settings `withLabel:process_single` matches labels `process_single` for process with name NFCORE_DMSCORE:DMSCORE:BAMFILTER_DMS +May-07 23:01:56.406 [main] DEBUG nextflow.script.ProcessConfig - Config settings `withName:BAMFILTER_DMS` matches process NFCORE_DMSCORE:DMSCORE:BAMFILTER_DMS +May-07 23:01:56.409 [main] DEBUG nextflow.executor.ExecutorFactory - << taskConfig executor: null +May-07 23:01:56.409 [main] DEBUG nextflow.executor.ExecutorFactory - >> processorType: 'local' +May-07 23:01:56.418 [main] DEBUG nextflow.script.ProcessConfig - Config settings `withLabel:process_medium` matches labels `process_medium` for process with name NFCORE_DMSCORE:DMSCORE:PREMERGE +May-07 23:01:56.418 [main] DEBUG nextflow.script.ProcessConfig - Config settings `withName:PREMERGE` matches process NFCORE_DMSCORE:DMSCORE:PREMERGE +May-07 23:01:56.420 [main] DEBUG nextflow.executor.ExecutorFactory - << taskConfig executor: null +May-07 23:01:56.420 [main] DEBUG nextflow.executor.ExecutorFactory - >> processorType: 'local' +May-07 23:01:56.429 [main] DEBUG nextflow.script.ProcessConfig - Config settings `withLabel:process_high` matches labels `process_high` for process with name NFCORE_DMSCORE:DMSCORE:GATK_SATURATIONMUTAGENESIS +May-07 23:01:56.430 [main] DEBUG nextflow.script.ProcessConfig - Config settings `withName:GATK_SATURATIONMUTAGENESIS` matches process NFCORE_DMSCORE:DMSCORE:GATK_SATURATIONMUTAGENESIS +May-07 23:01:56.433 [main] DEBUG nextflow.executor.ExecutorFactory - << taskConfig executor: null +May-07 23:01:56.433 [main] DEBUG nextflow.executor.ExecutorFactory - >> processorType: 'local' +May-07 23:01:56.443 [main] DEBUG nextflow.script.ProcessConfig - Config settings `withLabel:process_single` matches labels `process_single` for process with name NFCORE_DMSCORE:DMSCORE:DMSANALYSIS_AASEQ +May-07 23:01:56.443 [main] DEBUG nextflow.script.ProcessConfig - Config settings `withName:DMSANALYSIS_AASEQ` matches process NFCORE_DMSCORE:DMSCORE:DMSANALYSIS_AASEQ +May-07 23:01:56.446 [main] DEBUG nextflow.executor.ExecutorFactory - << taskConfig executor: null +May-07 23:01:56.446 [main] DEBUG nextflow.executor.ExecutorFactory - >> processorType: 'local' +May-07 23:01:56.458 [main] DEBUG nextflow.script.ProcessConfig - Config settings `withLabel:process_single` matches labels `process_single` for process with name NFCORE_DMSCORE:DMSCORE:DMSANALYSIS_POSSIBLE_MUTATIONS +May-07 23:01:56.459 [main] DEBUG nextflow.script.ProcessConfig - Config settings `withName:DMSANALYSIS_POSSIBLE_MUTATIONS` matches process NFCORE_DMSCORE:DMSCORE:DMSANALYSIS_POSSIBLE_MUTATIONS +May-07 23:01:56.461 [main] DEBUG nextflow.executor.ExecutorFactory - << taskConfig executor: null +May-07 23:01:56.461 [main] DEBUG nextflow.executor.ExecutorFactory - >> processorType: 'local' +May-07 23:01:56.480 [main] DEBUG nextflow.script.ProcessConfig - Config settings `withLabel:process_single` matches labels `process_single` for process with name NFCORE_DMSCORE:DMSCORE:DMSANALYSIS_PROCESS_GATK +May-07 23:01:56.481 [main] DEBUG nextflow.script.ProcessConfig - Config settings `withName:DMSANALYSIS_PROCESS_GATK` matches process NFCORE_DMSCORE:DMSCORE:DMSANALYSIS_PROCESS_GATK +May-07 23:01:56.484 [main] DEBUG nextflow.executor.ExecutorFactory - << taskConfig executor: null +May-07 23:01:56.484 [main] DEBUG nextflow.executor.ExecutorFactory - >> processorType: 'local' +May-07 23:01:56.499 [main] DEBUG nextflow.Session - Config process names validation disabled as requested +May-07 23:01:56.500 [main] DEBUG nextflow.Session - Igniting dataflow network (43) +May-07 23:01:56.508 [main] DEBUG nextflow.processor.TaskProcessor - Starting process > NFCORE_DMSCORE:DMSCORE:FASTQC +May-07 23:01:56.508 [main] DEBUG nextflow.processor.TaskProcessor - Starting process > NFCORE_DMSCORE:DMSCORE:MULTIQC +May-07 23:01:56.509 [main] DEBUG nextflow.processor.TaskProcessor - Starting process > NFCORE_DMSCORE:DMSCORE:BWA_INDEX +May-07 23:01:56.509 [main] DEBUG nextflow.processor.TaskProcessor - Starting process > NFCORE_DMSCORE:DMSCORE:BWA_MEM +May-07 23:01:56.511 [main] DEBUG nextflow.processor.TaskProcessor - Starting process > NFCORE_DMSCORE:DMSCORE:BAMFILTER_DMS +May-07 23:01:56.512 [main] DEBUG nextflow.processor.TaskProcessor - Starting process > NFCORE_DMSCORE:DMSCORE:PREMERGE +May-07 23:01:56.512 [main] DEBUG nextflow.processor.TaskProcessor - Starting process > NFCORE_DMSCORE:DMSCORE:GATK_SATURATIONMUTAGENESIS +May-07 23:01:56.512 [main] DEBUG nextflow.processor.TaskProcessor - Starting process > NFCORE_DMSCORE:DMSCORE:DMSANALYSIS_AASEQ +May-07 23:01:56.512 [main] DEBUG nextflow.processor.TaskProcessor - Starting process > NFCORE_DMSCORE:DMSCORE:DMSANALYSIS_POSSIBLE_MUTATIONS +May-07 23:01:56.512 [main] DEBUG nextflow.processor.TaskProcessor - Starting process > NFCORE_DMSCORE:DMSCORE:DMSANALYSIS_PROCESS_GATK +May-07 23:01:56.514 [main] DEBUG nextflow.script.ScriptRunner - Parsed script files: + Script_e5f965e09aa3641b: /Users/benjaminwehnert/dmscore/./workflows/../modules/local/bamprocessing/premerge.nf + Script_113f045ba19c2c46: /Users/benjaminwehnert/dmscore/./workflows/../modules/nf-core/fastqc/main.nf + Script_4f612cfd8c52e8cd: /Users/benjaminwehnert/dmscore/./subworkflows/local/utils_nfcore_dmscore_pipeline/../../nf-core/utils_nextflow_pipeline/main.nf + Script_215a636da7ab24a2: /Users/benjaminwehnert/dmscore/./workflows/../subworkflows/local/utils_nfcore_dmscore_pipeline/../../nf-core/utils_nfschema_plugin/main.nf + Script_68606fccdecc54d9: /Users/benjaminwehnert/dmscore/./subworkflows/local/utils_nfcore_dmscore_pipeline/main.nf + Script_0f3077e98f6a93f6: /Users/benjaminwehnert/dmscore/./workflows/../modules/nf-core/bwa/mem/main.nf + Script_5c4e8d4051efa81e: /Users/benjaminwehnert/dmscore/./subworkflows/local/utils_nfcore_dmscore_pipeline/../../nf-core/utils_nfcore_pipeline/main.nf + Script_3bf8120d0b5bcde1: /Users/benjaminwehnert/dmscore/./workflows/../modules/local/dmsanalysis/possiblemutations.nf + Script_d65a0ba0a319d7db: /Users/benjaminwehnert/dmscore/./workflows/../modules/local/dmsanalysis/aaseq.nf + Script_4ff7366a79e0ef06: /Users/benjaminwehnert/dmscore/./workflows/../modules/local/bamprocessing/bamfilteringdms.nf + Script_c568aae5b239c1c5: /Users/benjaminwehnert/dmscore/main.nf + Script_09ccfa79b2802f41: /Users/benjaminwehnert/dmscore/./workflows/dmscore.nf + Script_4955288afd8ca61e: /Users/benjaminwehnert/dmscore/./workflows/../modules/nf-core/multiqc/main.nf + Script_a1878766b1a6b241: /Users/benjaminwehnert/dmscore/./workflows/../modules/local/dmsanalysis/processgatk.nf + Script_37dcd664b2773148: /Users/benjaminwehnert/dmscore/./workflows/../modules/local/gatk/saturationmutagenesis.nf + Script_25aa31c18d513c61: /Users/benjaminwehnert/dmscore/./workflows/../modules/nf-core/bwa/index/main.nf +May-07 23:01:56.514 [main] DEBUG nextflow.script.ScriptRunner - > Awaiting termination +May-07 23:01:56.514 [main] DEBUG nextflow.Session - Session await +May-07 23:01:56.605 [Actor Thread 1] DEBUG nextflow.sort.BigSort - Sort completed -- entries: 1; slices: 1; internal sort time: 0.001 s; external sort time: 0.011 s; total time: 0.012 s +May-07 23:01:56.605 [Actor Thread 2] DEBUG nextflow.sort.BigSort - Sort completed -- entries: 1; slices: 1; internal sort time: 0.001 s; external sort time: 0.011 s; total time: 0.012 s +May-07 23:01:56.611 [Actor Thread 2] DEBUG nextflow.file.FileCollector - Saved collect-files list to: /Users/benjaminwehnert/dmscore/work/collect-file/4640128d9cfd4a45636425a4f43db374 +May-07 23:01:56.611 [Actor Thread 1] DEBUG nextflow.file.FileCollector - Saved collect-files list to: /Users/benjaminwehnert/dmscore/work/collect-file/04a58cc29ef1c76e5af4e2fb20a13ad7 +May-07 23:01:56.619 [Actor Thread 1] DEBUG nextflow.file.FileCollector - Deleting file collector temp dir: /var/folders/r0/ldrzd4wn1s3516hy0vsn8xzm0000gn/T/nxf-1651345332501275985 +May-07 23:01:56.619 [Actor Thread 2] DEBUG nextflow.file.FileCollector - Deleting file collector temp dir: /var/folders/r0/ldrzd4wn1s3516hy0vsn8xzm0000gn/T/nxf-12321695686053957190 +May-07 23:01:56.623 [Actor Thread 3] DEBUG nextflow.util.HashBuilder - [WARN] Unknown hashing type: class Script_09ccfa79b2802f41$_runScript_closure1$_closure26 +May-07 23:01:56.624 [Actor Thread 14] DEBUG nextflow.util.HashBuilder - Unable to get file attributes file: /NULL -- Cause: java.nio.file.NoSuchFileException: /NULL +May-07 23:01:56.697 [Task submitter] DEBUG n.executor.local.LocalTaskHandler - Launch cmd line: /bin/bash -ue .command.run +May-07 23:01:56.698 [Task submitter] INFO nextflow.Session - [d4/e141d7] Submitted process > NFCORE_DMSCORE:DMSCORE:DMSANALYSIS_AASEQ (amino_acid_sequence) +May-07 23:01:56.730 [FileTransfer-2] DEBUG nextflow.file.FilePorter - Copying foreign file https://raw.githubusercontent.com/BenjaminWehnert1008/test-datasets/dmsqc/dmsqc/pMS190_GID1A_SUNi_S2_1_R2_50k.fastq to work dir: /Users/benjaminwehnert/dmscore/work/stage-2397b75e-3882-46d7-ba2c-8549f8a2b4a6/6a/8815724cee5e2e4e17200e98e8c0ad/pMS190_GID1A_SUNi_S2_1_R2_50k.fastq +May-07 23:01:56.730 [FileTransfer-1] DEBUG nextflow.file.FilePorter - Copying foreign file https://raw.githubusercontent.com/BenjaminWehnert1008/test-datasets/dmsqc/dmsqc/pMS190_GID1A_SUNi_S2_1_R1_50k.fastq to work dir: /Users/benjaminwehnert/dmscore/work/stage-2397b75e-3882-46d7-ba2c-8549f8a2b4a6/40/dfe72a8fa5ce1b038fdf1aa1fb4749/pMS190_GID1A_SUNi_S2_1_R1_50k.fastq +May-07 23:01:58.744 [Actor Thread 15] INFO nextflow.file.FilePorter - Staging foreign file: https://raw.githubusercontent.com/BenjaminWehnert1008/test-datasets/dmsqc/dmsqc/pMS190_GID1A_SUNi_S2_1_R1_50k.fastq +May-07 23:02:00.581 [Task monitor] DEBUG n.processor.TaskPollingMonitor - Task completed > TaskHandler[id: 3; name: NFCORE_DMSCORE:DMSCORE:DMSANALYSIS_AASEQ (amino_acid_sequence); status: COMPLETED; exit: 0; error: -; workDir: /Users/benjaminwehnert/dmscore/work/d4/e141d76a7c3b4130080bcdf8a831a3] +May-07 23:02:00.585 [Task monitor] DEBUG nextflow.util.ThreadPoolBuilder - Creating thread pool 'TaskFinalizer' minSize=10; maxSize=24; workQueue=LinkedBlockingQueue[10000]; allowCoreThreadTimeout=false +May-07 23:02:00.612 [Task submitter] DEBUG n.executor.local.LocalTaskHandler - Launch cmd line: /bin/bash -ue .command.run +May-07 23:02:00.613 [Task submitter] INFO nextflow.Session - [49/349fd9] Submitted process > NFCORE_DMSCORE:DMSCORE:BWA_INDEX (GID1A_SUNi_ref_small.fasta) +May-07 23:02:00.663 [TaskFinalizer-1] DEBUG nextflow.util.ThreadPoolBuilder - Creating thread pool 'PublishDir' minSize=10; maxSize=24; workQueue=LinkedBlockingQueue[10000]; allowCoreThreadTimeout=false +May-07 23:02:00.756 [Actor Thread 15] INFO nextflow.file.FilePorter - Staging foreign file: https://raw.githubusercontent.com/BenjaminWehnert1008/test-datasets/dmsqc/dmsqc/pMS190_GID1A_SUNi_S2_1_R2_50k.fastq +May-07 23:02:00.757 [Actor Thread 4] WARN nextflow.processor.TaskContext - Cannot serialize context map. Cause: java.lang.IllegalArgumentException: Unable to create serializer "com.esotericsoftware.kryo.serializers.FieldSerializer" for class: java.lang.ref.ReferenceQueue -- Resume will not work on this process +May-07 23:02:00.763 [Actor Thread 4] DEBUG nextflow.processor.TaskContext - Failed to serialize delegate map items: [ + 'meta':[Script_09ccfa79b2802f41$_runScript_closure1$_closure26] = + 'pos_range':[java.lang.String] = 352-1383 + '$':[java.lang.Boolean] = true + 'wt_seq':[nextflow.processor.TaskPath] = GID1A_SUNi_ref_small.fasta + 'script':[nextflow.processor.TaskPath] = aa_seq.R + 'task':[nextflow.processor.TaskConfig] = [container:community.wave.seqera.io/library/bioconductor-biostrings_r-base_r-biocmanager_r-dplyr_pruned:0fd2e39a5bf2ecaa, withName:PREMERGE:[publishDir:[path:./results/intermediate_files/bam_files, mode:copy, saveAs:ScriptE8646A4B8FFA7429020F836DC9CB8146$_run_closure1$_closure9$_closure21@26dc7ffc]], memory:Script126E8BAA3BC9692B58F2A1C965A1D472$_run_closure1$_closure6$_closure15@13f9d575, withName:FASTQC:[ext:[args:--quiet], containerOptions:], withLabel:error_retry:[errorStrategy:retry, maxRetries:2], when:nextflow.script.TaskClosure@264d7648, withLabel:process_high:[cpus:Script126E8BAA3BC9692B58F2A1C965A1D472$_run_closure1$_closure9$_closure23@1b36f3f3, memory:Script126E8BAA3BC9692B58F2A1C965A1D472$_run_closure1$_closure9$_closure24@5e504cf6, time:Script126E8BAA3BC9692B58F2A1C965A1D472$_run_closure1$_closure9$_closure25@35f6834], withLabel:error_ignore:[errorStrategy:ignore], resourceLimits:[cpus:4, memory:8.GB, time:1.h], withName:DMSANALYSIS_AASEQ:[publishDir:[path:./results/intermediate_files, mode:copy, saveAs:ScriptE8646A4B8FFA7429020F836DC9CB8146$_run_closure1$_closure13$_closure25@4eeda121]], withLabel:process_high_memory:[memory:Script126E8BAA3BC9692B58F2A1C965A1D472$_run_closure1$_closure11$_closure27@62e86a64], withName:BAMFILTER_DMS:[publishDir:[path:./results/intermediate_files/bam_files, mode:copy, saveAs:ScriptE8646A4B8FFA7429020F836DC9CB8146$_run_closure1$_closure8$_closure20@3e03bd33]], publishDir:[[path:./results/intermediate_files, mode:copy, saveAs:ScriptE8646A4B8FFA7429020F836DC9CB8146$_run_closure1$_closure13$_closure25@4eeda121]], withName:BWA_MEM:[publishDir:[path:./results/intermediate_files/bam_files/bwa/mem, mode:copy, saveAs:ScriptE8646A4B8FFA7429020F836DC9CB8146$_run_closure1$_closure7$_closure19@46e56c0f]], executor:local, stub:nextflow.script.TaskClosure@6522395b, conda:null/environment.yml, withName:GATK_SATURATIONMUTAGENESIS:[publishDir:[path:./results/intermediate_files/gatk, mode:copy, saveAs:ScriptE8646A4B8FFA7429020F836DC9CB8146$_run_closure1$_closure10$_closure22@4438d4d1]], cacheable:true, withLabel:process_low:[cpus:Script126E8BAA3BC9692B58F2A1C965A1D472$_run_closure1$_closure7$_closure17@67f11340, memory:Script126E8BAA3BC9692B58F2A1C965A1D472$_run_closure1$_closure7$_closure18@a998ea5, time:Script126E8BAA3BC9692B58F2A1C965A1D472$_run_closure1$_closure7$_closure19@7e85964c], withLabel:process_medium:[cpus:Script126E8BAA3BC9692B58F2A1C965A1D472$_run_closure1$_closure8$_closure20@7c995b11, memory:Script126E8BAA3BC9692B58F2A1C965A1D472$_run_closure1$_closure8$_closure21@131d3cd1, time:Script126E8BAA3BC9692B58F2A1C965A1D472$_run_closure1$_closure8$_closure22@55b774b1], tag:amino_acid_sequence, withName:MULTIQC:[ext:[args:ScriptE8646A4B8FFA7429020F836DC9CB8146$_run_closure1$_closure5$_closure15@7d2bfbd], publishDir:[path:ScriptE8646A4B8FFA7429020F836DC9CB8146$_run_closure1$_closure5$_closure16@31a52d85, mode:copy, saveAs:ScriptE8646A4B8FFA7429020F836DC9CB8146$_run_closure1$_closure5$_closure17@4ba464d4]], workDir:/Users/benjaminwehnert/dmscore/work/d4/e141d76a7c3b4130080bcdf8a831a3, exitStatus:0, ext:[:], withLabel:process_single:[cpus:Script126E8BAA3BC9692B58F2A1C965A1D472$_run_closure1$_closure6$_closure14@255893ed, memory:Script126E8BAA3BC9692B58F2A1C965A1D472$_run_closure1$_closure6$_closure15@13f9d575, time:Script126E8BAA3BC9692B58F2A1C965A1D472$_run_closure1$_closure6$_closure16@37e5efac], withName:BWA_INDEX:[publishDir:[path:./results/intermediate_files/bam_files, mode:copy, saveAs:ScriptE8646A4B8FFA7429020F836DC9CB8146$_run_closure1$_closure6$_closure18@2f3435d0]], process:NFCORE_DMSCORE:DMSCORE:DMSANALYSIS_AASEQ, debug:false, cpus:Script126E8BAA3BC9692B58F2A1C965A1D472$_run_closure1$_closure6$_closure14@255893ed, index:1, label:[process_single], withLabel:process_long:[time:Script126E8BAA3BC9692B58F2A1C965A1D472$_run_closure1$_closure10$_closure26@475e6626], maxRetries:1, maxErrors:-1, shell:bash + +set -e # Exit if a tool returns a non-zero status/exit code +set -u # Treat unset variables and parameters as an error +set -o pipefail # Returns the status of the last command to exit with a non-zero status or zero if all successfully execute +set -C # No clobber - prevent output redirection from overwriting files. +, withName:DMSANALYSIS_POSSIBLE_MUTATIONS:[publishDir:[path:./results/intermediate_files, mode:copy, saveAs:ScriptE8646A4B8FFA7429020F836DC9CB8146$_run_closure1$_closure11$_closure23@267852db]], name:NFCORE_DMSCORE:DMSCORE:DMSANALYSIS_AASEQ (amino_acid_sequence), containerOptions:-u $(id -u):$(id -g), errorStrategy:Script126E8BAA3BC9692B58F2A1C965A1D472$_run_closure1$_closure5@3e785137, time:Script126E8BAA3BC9692B58F2A1C965A1D472$_run_closure1$_closure6$_closure16@37e5efac, withName:DMSANALYSIS_PROCESS_GATK:[publishDir:[path:./results/intermediate_files/processed_gatk_files, mode:copy, saveAs:ScriptE8646A4B8FFA7429020F836DC9CB8146$_run_closure1$_closure14$_closure26@30ec799d]], hash:d4e141d76a7c3b4130080bcdf8a831a3] +] +com.esotericsoftware.kryo.KryoException: java.lang.IllegalArgumentException: Unable to create serializer "com.esotericsoftware.kryo.serializers.FieldSerializer" for class: java.lang.ref.ReferenceQueue +Serialization trace: +queue (org.codehaus.groovy.util.ReferenceManager$CallBackedManager) +manager (org.codehaus.groovy.util.ReferenceManager$1) +manager (org.codehaus.groovy.util.ReferenceBundle) +bundle (org.codehaus.groovy.reflection.CachedClass$4) +cachedSuperClass (org.codehaus.groovy.reflection.stdclasses.ObjectCachedClass) +cachedClass (org.codehaus.groovy.reflection.CachedMethod) +allMethods (groovy.lang.MetaClassImpl) +delegate (groovy.runtime.metaclass.NextflowDelegatingMetaClass) +metaClass (groovyx.gpars.dataflow.DataflowVariable) +first (groovyx.gpars.dataflow.stream.DataflowStream) +asyncHead (groovyx.gpars.dataflow.stream.DataflowStreamReadAdapter) +source (nextflow.extension.MapOp) +owner (nextflow.extension.MapOp$_apply_closure1) +code (groovyx.gpars.dataflow.operator.DataflowOperatorActor) +actor (groovyx.gpars.dataflow.operator.DataflowOperator) +allOperators (nextflow.Session) +session (nextflow.validation.ValidationExtension) +target (nextflow.script.FunctionDef) +definitions (nextflow.script.ScriptMeta) +meta (nextflow.script.ScriptBinding) +binding (Script_09ccfa79b2802f41) +delegate (Script_09ccfa79b2802f41$_runScript_closure1) +delegate (Script_09ccfa79b2802f41$_runScript_closure1$_closure26) + at com.esotericsoftware.kryo.serializers.ObjectField.write(ObjectField.java:82) + at com.esotericsoftware.kryo.serializers.FieldSerializer.write(FieldSerializer.java:495) + at com.esotericsoftware.kryo.Kryo.writeObject(Kryo.java:523) + at com.esotericsoftware.kryo.serializers.ObjectField.write(ObjectField.java:61) + at com.esotericsoftware.kryo.serializers.FieldSerializer.write(FieldSerializer.java:495) + at com.esotericsoftware.kryo.Kryo.writeObject(Kryo.java:523) + at com.esotericsoftware.kryo.serializers.ObjectField.write(ObjectField.java:61) + at com.esotericsoftware.kryo.serializers.FieldSerializer.write(FieldSerializer.java:495) + at com.esotericsoftware.kryo.Kryo.writeObject(Kryo.java:523) + at com.esotericsoftware.kryo.serializers.ObjectField.write(ObjectField.java:61) + at com.esotericsoftware.kryo.serializers.FieldSerializer.write(FieldSerializer.java:495) + at com.esotericsoftware.kryo.Kryo.writeObject(Kryo.java:523) + at com.esotericsoftware.kryo.serializers.ObjectField.write(ObjectField.java:61) + at com.esotericsoftware.kryo.serializers.FieldSerializer.write(FieldSerializer.java:495) + at com.esotericsoftware.kryo.Kryo.writeObject(Kryo.java:523) + at com.esotericsoftware.kryo.serializers.ObjectField.write(ObjectField.java:61) + at com.esotericsoftware.kryo.serializers.FieldSerializer.write(FieldSerializer.java:495) + at com.esotericsoftware.kryo.Kryo.writeClassAndObject(Kryo.java:599) + at com.esotericsoftware.kryo.serializers.CollectionSerializer.write(CollectionSerializer.java:82) + at com.esotericsoftware.kryo.serializers.CollectionSerializer.write(CollectionSerializer.java:22) + at com.esotericsoftware.kryo.Kryo.writeObject(Kryo.java:523) + at com.esotericsoftware.kryo.serializers.ObjectField.write(ObjectField.java:61) + at com.esotericsoftware.kryo.serializers.FieldSerializer.write(FieldSerializer.java:495) + at com.esotericsoftware.kryo.Kryo.writeObject(Kryo.java:523) + at com.esotericsoftware.kryo.serializers.ObjectField.write(ObjectField.java:61) + at com.esotericsoftware.kryo.serializers.FieldSerializer.write(FieldSerializer.java:495) + at com.esotericsoftware.kryo.Kryo.writeObject(Kryo.java:523) + at com.esotericsoftware.kryo.serializers.ObjectField.write(ObjectField.java:61) + at com.esotericsoftware.kryo.serializers.FieldSerializer.write(FieldSerializer.java:495) + at com.esotericsoftware.kryo.Kryo.writeObject(Kryo.java:523) + at com.esotericsoftware.kryo.serializers.ObjectField.write(ObjectField.java:61) + at com.esotericsoftware.kryo.serializers.FieldSerializer.write(FieldSerializer.java:495) + at com.esotericsoftware.kryo.Kryo.writeObject(Kryo.java:523) + at com.esotericsoftware.kryo.serializers.ObjectField.write(ObjectField.java:61) + at com.esotericsoftware.kryo.serializers.FieldSerializer.write(FieldSerializer.java:495) + at com.esotericsoftware.kryo.Kryo.writeObject(Kryo.java:523) + at com.esotericsoftware.kryo.serializers.ObjectField.write(ObjectField.java:61) + at com.esotericsoftware.kryo.serializers.FieldSerializer.write(FieldSerializer.java:495) + at com.esotericsoftware.kryo.Kryo.writeObject(Kryo.java:523) + at com.esotericsoftware.kryo.serializers.ObjectField.write(ObjectField.java:61) + at com.esotericsoftware.kryo.serializers.FieldSerializer.write(FieldSerializer.java:495) + at com.esotericsoftware.kryo.Kryo.writeObject(Kryo.java:523) + at com.esotericsoftware.kryo.serializers.ObjectField.write(ObjectField.java:61) + at com.esotericsoftware.kryo.serializers.FieldSerializer.write(FieldSerializer.java:495) + at com.esotericsoftware.kryo.Kryo.writeObject(Kryo.java:523) + at com.esotericsoftware.kryo.serializers.ObjectField.write(ObjectField.java:61) + at com.esotericsoftware.kryo.serializers.FieldSerializer.write(FieldSerializer.java:495) + at com.esotericsoftware.kryo.Kryo.writeClassAndObject(Kryo.java:599) + at com.esotericsoftware.kryo.serializers.CollectionSerializer.write(CollectionSerializer.java:82) + at com.esotericsoftware.kryo.serializers.CollectionSerializer.write(CollectionSerializer.java:22) + at com.esotericsoftware.kryo.Kryo.writeObject(Kryo.java:523) + at com.esotericsoftware.kryo.serializers.ObjectField.write(ObjectField.java:61) + at com.esotericsoftware.kryo.serializers.FieldSerializer.write(FieldSerializer.java:495) + at com.esotericsoftware.kryo.Kryo.writeObject(Kryo.java:523) + at com.esotericsoftware.kryo.serializers.ObjectField.write(ObjectField.java:61) + at com.esotericsoftware.kryo.serializers.FieldSerializer.write(FieldSerializer.java:495) + at com.esotericsoftware.kryo.Kryo.writeObject(Kryo.java:523) + at com.esotericsoftware.kryo.serializers.ObjectField.write(ObjectField.java:61) + at com.esotericsoftware.kryo.serializers.FieldSerializer.write(FieldSerializer.java:495) + at com.esotericsoftware.kryo.Kryo.writeClassAndObject(Kryo.java:599) + at com.esotericsoftware.kryo.serializers.MapSerializer.write(MapSerializer.java:95) + at com.esotericsoftware.kryo.serializers.MapSerializer.write(MapSerializer.java:21) + at com.esotericsoftware.kryo.Kryo.writeObject(Kryo.java:523) + at com.esotericsoftware.kryo.serializers.ObjectField.write(ObjectField.java:61) + at com.esotericsoftware.kryo.serializers.FieldSerializer.write(FieldSerializer.java:495) + at com.esotericsoftware.kryo.Kryo.writeObject(Kryo.java:523) + at com.esotericsoftware.kryo.serializers.ObjectField.write(ObjectField.java:61) + at com.esotericsoftware.kryo.serializers.FieldSerializer.write(FieldSerializer.java:495) + at com.esotericsoftware.kryo.Kryo.writeObject(Kryo.java:523) + at com.esotericsoftware.kryo.serializers.ObjectField.write(ObjectField.java:61) + at com.esotericsoftware.kryo.serializers.FieldSerializer.write(FieldSerializer.java:495) + at com.esotericsoftware.kryo.Kryo.writeObject(Kryo.java:523) + at com.esotericsoftware.kryo.serializers.ObjectField.write(ObjectField.java:61) + at com.esotericsoftware.kryo.serializers.FieldSerializer.write(FieldSerializer.java:495) + at com.esotericsoftware.kryo.Kryo.writeObject(Kryo.java:523) + at com.esotericsoftware.kryo.serializers.ObjectField.write(ObjectField.java:61) + at com.esotericsoftware.kryo.serializers.FieldSerializer.write(FieldSerializer.java:495) + at com.esotericsoftware.kryo.Kryo.writeClassAndObject(Kryo.java:599) + at com.esotericsoftware.kryo.serializers.MapSerializer.write(MapSerializer.java:95) + at com.esotericsoftware.kryo.serializers.MapSerializer.write(MapSerializer.java:21) + at com.esotericsoftware.kryo.Kryo.writeClassAndObject(Kryo.java:599) + at org.codehaus.groovy.vmplugin.v8.IndyInterface.fromCache(IndyInterface.java:321) + at nextflow.util.KryoHelper.serialize(SerializationHelper.groovy:166) + at org.codehaus.groovy.vmplugin.v8.IndyInterface.fromCache(IndyInterface.java:321) + at nextflow.processor.TaskContext.serialize(TaskContext.groovy:198) + at nextflow.cache.CacheDB.writeTaskEntry0(CacheDB.groovy:148) + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) + at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) + at java.base/java.lang.reflect.Method.invoke(Method.java:569) + at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:343) + at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:328) + at groovy.lang.MetaClassImpl.doInvokeMethod(MetaClassImpl.java:1333) + at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1088) + at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1007) + at org.codehaus.groovy.runtime.InvokerHelper.invokePogoMethod(InvokerHelper.java:645) + at org.codehaus.groovy.runtime.InvokerHelper.invokeMethod(InvokerHelper.java:628) + at org.codehaus.groovy.runtime.InvokerHelper.invokeMethodSafe(InvokerHelper.java:82) + at nextflow.cache.CacheDB$_putTaskAsync_closure1.doCall(CacheDB.groovy:157) + at nextflow.cache.CacheDB$_putTaskAsync_closure1.call(CacheDB.groovy) + at groovyx.gpars.agent.AgentBase.onMessage(AgentBase.java:102) + at groovyx.gpars.agent.Agent.handleMessage(Agent.java:84) + at groovyx.gpars.agent.AgentCore$1.handleMessage(AgentCore.java:48) + at groovyx.gpars.util.AsyncMessagingCore.run(AsyncMessagingCore.java:132) + at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136) + at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635) + at java.base/java.lang.Thread.run(Thread.java:840) +Caused by: java.lang.IllegalArgumentException: Unable to create serializer "com.esotericsoftware.kryo.serializers.FieldSerializer" for class: java.lang.ref.ReferenceQueue + at com.esotericsoftware.kryo.factories.ReflectionSerializerFactory.makeSerializer(ReflectionSerializerFactory.java:48) + at com.esotericsoftware.kryo.factories.ReflectionSerializerFactory.makeSerializer(ReflectionSerializerFactory.java:26) + at com.esotericsoftware.kryo.Kryo.newDefaultSerializer(Kryo.java:351) + at com.esotericsoftware.kryo.Kryo.getDefaultSerializer(Kryo.java:344) + at com.esotericsoftware.kryo.util.DefaultClassResolver.registerImplicit(DefaultClassResolver.java:56) + at com.esotericsoftware.kryo.Kryo.getRegistration(Kryo.java:461) + at com.esotericsoftware.kryo.util.DefaultClassResolver.writeClass(DefaultClassResolver.java:79) + at com.esotericsoftware.kryo.Kryo.writeClass(Kryo.java:488) + at com.esotericsoftware.kryo.serializers.ObjectField.write(ObjectField.java:57) + ... 106 common frames omitted +Caused by: java.lang.reflect.InvocationTargetException: null + at jdk.internal.reflect.GeneratedConstructorAccessor39.newInstance(Unknown Source) + at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) + at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:500) + at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:481) + at com.esotericsoftware.kryo.factories.ReflectionSerializerFactory.makeSerializer(ReflectionSerializerFactory.java:35) + ... 114 common frames omitted +Caused by: java.lang.reflect.InaccessibleObjectException: Unable to make field private final java.lang.ref.ReferenceQueue$Lock java.lang.ref.ReferenceQueue.lock accessible: module java.base does not "opens java.lang.ref" to unnamed module @7b02881e + at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:354) + at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:297) + at java.base/java.lang.reflect.Field.checkCanSetAccessible(Field.java:178) + at java.base/java.lang.reflect.Field.setAccessible(Field.java:172) + at com.esotericsoftware.kryo.serializers.FieldSerializer.buildValidFields(FieldSerializer.java:282) + at com.esotericsoftware.kryo.serializers.FieldSerializer.rebuildCachedFields(FieldSerializer.java:217) + at com.esotericsoftware.kryo.serializers.FieldSerializer.rebuildCachedFields(FieldSerializer.java:156) + at com.esotericsoftware.kryo.serializers.FieldSerializer.(FieldSerializer.java:133) + ... 119 common frames omitted +May-07 23:02:02.378 [Task monitor] DEBUG n.processor.TaskPollingMonitor - Task completed > TaskHandler[id: 1; name: NFCORE_DMSCORE:DMSCORE:BWA_INDEX (GID1A_SUNi_ref_small.fasta); status: COMPLETED; exit: 0; error: -; workDir: /Users/benjaminwehnert/dmscore/work/49/349fd93ff7dd98e92e426a30457fb9] +May-07 23:02:02.387 [Task submitter] DEBUG n.executor.local.LocalTaskHandler - Launch cmd line: /bin/bash -ue .command.run +May-07 23:02:02.388 [Task submitter] INFO nextflow.Session - [36/b138e8] Submitted process > NFCORE_DMSCORE:DMSCORE:DMSANALYSIS_POSSIBLE_MUTATIONS (table /w all possible variants) +May-07 23:02:02.422 [Actor Thread 8] WARN nextflow.processor.TaskContext - Cannot serialize context map. Cause: java.lang.IllegalArgumentException: Unable to create serializer "com.esotericsoftware.kryo.serializers.FieldSerializer" for class: java.lang.ref.ReferenceQueue -- Resume will not work on this process +May-07 23:02:13.067 [Task monitor] DEBUG n.processor.TaskPollingMonitor - Task completed > TaskHandler[id: 2; name: NFCORE_DMSCORE:DMSCORE:DMSANALYSIS_POSSIBLE_MUTATIONS (table /w all possible variants); status: COMPLETED; exit: 0; error: -; workDir: /Users/benjaminwehnert/dmscore/work/36/b138e8e27f7423eea98b17aaf74451] +May-07 23:02:13.085 [Task submitter] DEBUG n.executor.local.LocalTaskHandler - Launch cmd line: /bin/bash -ue .command.run +May-07 23:02:13.085 [Task submitter] INFO nextflow.Session - [67/c2a06b] Submitted process > NFCORE_DMSCORE:DMSCORE:BWA_MEM (gid1a_1_quality_1_pe) +May-07 23:02:13.133 [Actor Thread 6] WARN nextflow.processor.TaskContext - Cannot serialize context map. Cause: java.lang.IllegalArgumentException: Unable to create serializer "com.esotericsoftware.kryo.serializers.FieldSerializer" for class: java.lang.ref.ReferenceQueue -- Resume will not work on this process +May-07 23:02:13.136 [Actor Thread 6] DEBUG nextflow.processor.TaskContext - Failed to serialize delegate map items: [ + 'meta':[Script_09ccfa79b2802f41$_runScript_closure1$_closure26] = + 'pos_range':[java.lang.String] = 352-1383 + 'mutagenesis_type':[java.lang.String] = max_diff_to_wt + '$':[java.lang.Boolean] = true + 'wt_seq':[nextflow.processor.TaskPath] = GID1A_SUNi_ref_small.fasta + 'custom_codon_library':[nextflow.processor.TaskPath] = NULL + 'script':[nextflow.processor.TaskPath] = possible_mutations.R + 'task':[nextflow.processor.TaskConfig] = [container:community.wave.seqera.io/library/bioconductor-biostrings_r-base_r-biocmanager_r-dplyr_pruned:0fd2e39a5bf2ecaa, withName:PREMERGE:[publishDir:[path:./results/intermediate_files/bam_files, mode:copy, saveAs:ScriptE8646A4B8FFA7429020F836DC9CB8146$_run_closure1$_closure9$_closure21@26dc7ffc]], memory:Script126E8BAA3BC9692B58F2A1C965A1D472$_run_closure1$_closure6$_closure15@13f9d575, withName:FASTQC:[ext:[args:--quiet], containerOptions:], withLabel:error_retry:[errorStrategy:retry, maxRetries:2], when:nextflow.script.TaskClosure@5f4899a5, withLabel:process_high:[cpus:Script126E8BAA3BC9692B58F2A1C965A1D472$_run_closure1$_closure9$_closure23@1b36f3f3, memory:Script126E8BAA3BC9692B58F2A1C965A1D472$_run_closure1$_closure9$_closure24@5e504cf6, time:Script126E8BAA3BC9692B58F2A1C965A1D472$_run_closure1$_closure9$_closure25@35f6834], withLabel:error_ignore:[errorStrategy:ignore], resourceLimits:[cpus:4, memory:8.GB, time:1.h], withName:DMSANALYSIS_AASEQ:[publishDir:[path:./results/intermediate_files, mode:copy, saveAs:ScriptE8646A4B8FFA7429020F836DC9CB8146$_run_closure1$_closure13$_closure25@4eeda121]], withLabel:process_high_memory:[memory:Script126E8BAA3BC9692B58F2A1C965A1D472$_run_closure1$_closure11$_closure27@62e86a64], withName:BAMFILTER_DMS:[publishDir:[path:./results/intermediate_files/bam_files, mode:copy, saveAs:ScriptE8646A4B8FFA7429020F836DC9CB8146$_run_closure1$_closure8$_closure20@3e03bd33]], publishDir:[[path:./results/intermediate_files, mode:copy, saveAs:ScriptE8646A4B8FFA7429020F836DC9CB8146$_run_closure1$_closure11$_closure23@267852db]], withName:BWA_MEM:[publishDir:[path:./results/intermediate_files/bam_files/bwa/mem, mode:copy, saveAs:ScriptE8646A4B8FFA7429020F836DC9CB8146$_run_closure1$_closure7$_closure19@46e56c0f]], executor:local, stub:nextflow.script.TaskClosure@7069093e, conda:null/environment.yml, withName:GATK_SATURATIONMUTAGENESIS:[publishDir:[path:./results/intermediate_files/gatk, mode:copy, saveAs:ScriptE8646A4B8FFA7429020F836DC9CB8146$_run_closure1$_closure10$_closure22@4438d4d1]], cacheable:true, withLabel:process_low:[cpus:Script126E8BAA3BC9692B58F2A1C965A1D472$_run_closure1$_closure7$_closure17@67f11340, memory:Script126E8BAA3BC9692B58F2A1C965A1D472$_run_closure1$_closure7$_closure18@a998ea5, time:Script126E8BAA3BC9692B58F2A1C965A1D472$_run_closure1$_closure7$_closure19@7e85964c], withLabel:process_medium:[cpus:Script126E8BAA3BC9692B58F2A1C965A1D472$_run_closure1$_closure8$_closure20@7c995b11, memory:Script126E8BAA3BC9692B58F2A1C965A1D472$_run_closure1$_closure8$_closure21@131d3cd1, time:Script126E8BAA3BC9692B58F2A1C965A1D472$_run_closure1$_closure8$_closure22@55b774b1], tag:table /w all possible variants, withName:MULTIQC:[ext:[args:ScriptE8646A4B8FFA7429020F836DC9CB8146$_run_closure1$_closure5$_closure15@7d2bfbd], publishDir:[path:ScriptE8646A4B8FFA7429020F836DC9CB8146$_run_closure1$_closure5$_closure16@31a52d85, mode:copy, saveAs:ScriptE8646A4B8FFA7429020F836DC9CB8146$_run_closure1$_closure5$_closure17@4ba464d4]], workDir:/Users/benjaminwehnert/dmscore/work/36/b138e8e27f7423eea98b17aaf74451, exitStatus:0, ext:[:], withLabel:process_single:[cpus:Script126E8BAA3BC9692B58F2A1C965A1D472$_run_closure1$_closure6$_closure14@255893ed, memory:Script126E8BAA3BC9692B58F2A1C965A1D472$_run_closure1$_closure6$_closure15@13f9d575, time:Script126E8BAA3BC9692B58F2A1C965A1D472$_run_closure1$_closure6$_closure16@37e5efac], withName:BWA_INDEX:[publishDir:[path:./results/intermediate_files/bam_files, mode:copy, saveAs:ScriptE8646A4B8FFA7429020F836DC9CB8146$_run_closure1$_closure6$_closure18@2f3435d0]], process:NFCORE_DMSCORE:DMSCORE:DMSANALYSIS_POSSIBLE_MUTATIONS, debug:false, cpus:Script126E8BAA3BC9692B58F2A1C965A1D472$_run_closure1$_closure6$_closure14@255893ed, index:1, label:[process_single], withLabel:process_long:[time:Script126E8BAA3BC9692B58F2A1C965A1D472$_run_closure1$_closure10$_closure26@475e6626], maxRetries:1, maxErrors:-1, shell:bash + +set -e # Exit if a tool returns a non-zero status/exit code +set -u # Treat unset variables and parameters as an error +set -o pipefail # Returns the status of the last command to exit with a non-zero status or zero if all successfully execute +set -C # No clobber - prevent output redirection from overwriting files. +, withName:DMSANALYSIS_POSSIBLE_MUTATIONS:[publishDir:[path:./results/intermediate_files, mode:copy, saveAs:ScriptE8646A4B8FFA7429020F836DC9CB8146$_run_closure1$_closure11$_closure23@267852db]], name:NFCORE_DMSCORE:DMSCORE:DMSANALYSIS_POSSIBLE_MUTATIONS (table /w all possible variants), containerOptions:-u $(id -u):$(id -g), errorStrategy:Script126E8BAA3BC9692B58F2A1C965A1D472$_run_closure1$_closure5@3e785137, time:Script126E8BAA3BC9692B58F2A1C965A1D472$_run_closure1$_closure6$_closure16@37e5efac, withName:DMSANALYSIS_PROCESS_GATK:[publishDir:[path:./results/intermediate_files/processed_gatk_files, mode:copy, saveAs:ScriptE8646A4B8FFA7429020F836DC9CB8146$_run_closure1$_closure14$_closure26@30ec799d]], hash:36b138e8e27f7423eea98b17aaf74451] +] +com.esotericsoftware.kryo.KryoException: java.lang.IllegalArgumentException: Unable to create serializer "com.esotericsoftware.kryo.serializers.FieldSerializer" for class: java.lang.ref.ReferenceQueue +Serialization trace: +queue (org.codehaus.groovy.util.ReferenceManager$CallBackedManager) +manager (org.codehaus.groovy.util.ReferenceManager$1) +manager (org.codehaus.groovy.util.ReferenceBundle) +bundle (org.codehaus.groovy.reflection.CachedClass$4) +cachedSuperClass (org.codehaus.groovy.reflection.stdclasses.ObjectCachedClass) +cachedClass (org.codehaus.groovy.reflection.CachedMethod) +allMethods (groovy.lang.MetaClassImpl) +delegate (groovy.runtime.metaclass.NextflowDelegatingMetaClass) +metaClass (groovyx.gpars.dataflow.DataflowVariable) +first (groovyx.gpars.dataflow.stream.DataflowStream) +asyncHead (groovyx.gpars.dataflow.stream.DataflowStreamReadAdapter) +source (nextflow.extension.MapOp) +owner (nextflow.extension.MapOp$_apply_closure1) +code (groovyx.gpars.dataflow.operator.DataflowOperatorActor) +actor (groovyx.gpars.dataflow.operator.DataflowOperator) +allOperators (nextflow.Session) +session (nextflow.validation.ValidationExtension) +target (nextflow.script.FunctionDef) +definitions (nextflow.script.ScriptMeta) +meta (nextflow.script.ScriptBinding) +binding (Script_09ccfa79b2802f41) +delegate (Script_09ccfa79b2802f41$_runScript_closure1) +delegate (Script_09ccfa79b2802f41$_runScript_closure1$_closure26) + at com.esotericsoftware.kryo.serializers.ObjectField.write(ObjectField.java:82) + at com.esotericsoftware.kryo.serializers.FieldSerializer.write(FieldSerializer.java:495) + at com.esotericsoftware.kryo.Kryo.writeObject(Kryo.java:523) + at com.esotericsoftware.kryo.serializers.ObjectField.write(ObjectField.java:61) + at com.esotericsoftware.kryo.serializers.FieldSerializer.write(FieldSerializer.java:495) + at com.esotericsoftware.kryo.Kryo.writeObject(Kryo.java:523) + at com.esotericsoftware.kryo.serializers.ObjectField.write(ObjectField.java:61) + at com.esotericsoftware.kryo.serializers.FieldSerializer.write(FieldSerializer.java:495) + at com.esotericsoftware.kryo.Kryo.writeObject(Kryo.java:523) + at com.esotericsoftware.kryo.serializers.ObjectField.write(ObjectField.java:61) + at com.esotericsoftware.kryo.serializers.FieldSerializer.write(FieldSerializer.java:495) + at com.esotericsoftware.kryo.Kryo.writeObject(Kryo.java:523) + at com.esotericsoftware.kryo.serializers.ObjectField.write(ObjectField.java:61) + at com.esotericsoftware.kryo.serializers.FieldSerializer.write(FieldSerializer.java:495) + at com.esotericsoftware.kryo.Kryo.writeObject(Kryo.java:523) + at com.esotericsoftware.kryo.serializers.ObjectField.write(ObjectField.java:61) + at com.esotericsoftware.kryo.serializers.FieldSerializer.write(FieldSerializer.java:495) + at com.esotericsoftware.kryo.Kryo.writeClassAndObject(Kryo.java:599) + at com.esotericsoftware.kryo.serializers.CollectionSerializer.write(CollectionSerializer.java:82) + at com.esotericsoftware.kryo.serializers.CollectionSerializer.write(CollectionSerializer.java:22) + at com.esotericsoftware.kryo.Kryo.writeObject(Kryo.java:523) + at com.esotericsoftware.kryo.serializers.ObjectField.write(ObjectField.java:61) + at com.esotericsoftware.kryo.serializers.FieldSerializer.write(FieldSerializer.java:495) + at com.esotericsoftware.kryo.Kryo.writeObject(Kryo.java:523) + at com.esotericsoftware.kryo.serializers.ObjectField.write(ObjectField.java:61) + at com.esotericsoftware.kryo.serializers.FieldSerializer.write(FieldSerializer.java:495) + at com.esotericsoftware.kryo.Kryo.writeObject(Kryo.java:523) + at com.esotericsoftware.kryo.serializers.ObjectField.write(ObjectField.java:61) + at com.esotericsoftware.kryo.serializers.FieldSerializer.write(FieldSerializer.java:495) + at com.esotericsoftware.kryo.Kryo.writeObject(Kryo.java:523) + at com.esotericsoftware.kryo.serializers.ObjectField.write(ObjectField.java:61) + at com.esotericsoftware.kryo.serializers.FieldSerializer.write(FieldSerializer.java:495) + at com.esotericsoftware.kryo.Kryo.writeObject(Kryo.java:523) + at com.esotericsoftware.kryo.serializers.ObjectField.write(ObjectField.java:61) + at com.esotericsoftware.kryo.serializers.FieldSerializer.write(FieldSerializer.java:495) + at com.esotericsoftware.kryo.Kryo.writeObject(Kryo.java:523) + at com.esotericsoftware.kryo.serializers.ObjectField.write(ObjectField.java:61) + at com.esotericsoftware.kryo.serializers.FieldSerializer.write(FieldSerializer.java:495) + at com.esotericsoftware.kryo.Kryo.writeObject(Kryo.java:523) + at com.esotericsoftware.kryo.serializers.ObjectField.write(ObjectField.java:61) + at com.esotericsoftware.kryo.serializers.FieldSerializer.write(FieldSerializer.java:495) + at com.esotericsoftware.kryo.Kryo.writeObject(Kryo.java:523) + at com.esotericsoftware.kryo.serializers.ObjectField.write(ObjectField.java:61) + at com.esotericsoftware.kryo.serializers.FieldSerializer.write(FieldSerializer.java:495) + at com.esotericsoftware.kryo.Kryo.writeObject(Kryo.java:523) + at com.esotericsoftware.kryo.serializers.ObjectField.write(ObjectField.java:61) + at com.esotericsoftware.kryo.serializers.FieldSerializer.write(FieldSerializer.java:495) + at com.esotericsoftware.kryo.Kryo.writeClassAndObject(Kryo.java:599) + at com.esotericsoftware.kryo.serializers.CollectionSerializer.write(CollectionSerializer.java:82) + at com.esotericsoftware.kryo.serializers.CollectionSerializer.write(CollectionSerializer.java:22) + at com.esotericsoftware.kryo.Kryo.writeObject(Kryo.java:523) + at com.esotericsoftware.kryo.serializers.ObjectField.write(ObjectField.java:61) + at com.esotericsoftware.kryo.serializers.FieldSerializer.write(FieldSerializer.java:495) + at com.esotericsoftware.kryo.Kryo.writeObject(Kryo.java:523) + at com.esotericsoftware.kryo.serializers.ObjectField.write(ObjectField.java:61) + at com.esotericsoftware.kryo.serializers.FieldSerializer.write(FieldSerializer.java:495) + at com.esotericsoftware.kryo.Kryo.writeObject(Kryo.java:523) + at com.esotericsoftware.kryo.serializers.ObjectField.write(ObjectField.java:61) + at com.esotericsoftware.kryo.serializers.FieldSerializer.write(FieldSerializer.java:495) + at com.esotericsoftware.kryo.Kryo.writeClassAndObject(Kryo.java:599) + at com.esotericsoftware.kryo.serializers.MapSerializer.write(MapSerializer.java:95) + at com.esotericsoftware.kryo.serializers.MapSerializer.write(MapSerializer.java:21) + at com.esotericsoftware.kryo.Kryo.writeObject(Kryo.java:523) + at com.esotericsoftware.kryo.serializers.ObjectField.write(ObjectField.java:61) + at com.esotericsoftware.kryo.serializers.FieldSerializer.write(FieldSerializer.java:495) + at com.esotericsoftware.kryo.Kryo.writeObject(Kryo.java:523) + at com.esotericsoftware.kryo.serializers.ObjectField.write(ObjectField.java:61) + at com.esotericsoftware.kryo.serializers.FieldSerializer.write(FieldSerializer.java:495) + at com.esotericsoftware.kryo.Kryo.writeObject(Kryo.java:523) + at com.esotericsoftware.kryo.serializers.ObjectField.write(ObjectField.java:61) + at com.esotericsoftware.kryo.serializers.FieldSerializer.write(FieldSerializer.java:495) + at com.esotericsoftware.kryo.Kryo.writeObject(Kryo.java:523) + at com.esotericsoftware.kryo.serializers.ObjectField.write(ObjectField.java:61) + at com.esotericsoftware.kryo.serializers.FieldSerializer.write(FieldSerializer.java:495) + at com.esotericsoftware.kryo.Kryo.writeObject(Kryo.java:523) + at com.esotericsoftware.kryo.serializers.ObjectField.write(ObjectField.java:61) + at com.esotericsoftware.kryo.serializers.FieldSerializer.write(FieldSerializer.java:495) + at com.esotericsoftware.kryo.Kryo.writeClassAndObject(Kryo.java:599) + at com.esotericsoftware.kryo.serializers.MapSerializer.write(MapSerializer.java:95) + at com.esotericsoftware.kryo.serializers.MapSerializer.write(MapSerializer.java:21) + at com.esotericsoftware.kryo.Kryo.writeClassAndObject(Kryo.java:599) + at org.codehaus.groovy.vmplugin.v8.IndyInterface.fromCache(IndyInterface.java:321) + at nextflow.util.KryoHelper.serialize(SerializationHelper.groovy:166) + at org.codehaus.groovy.vmplugin.v8.IndyInterface.fromCache(IndyInterface.java:321) + at nextflow.processor.TaskContext.serialize(TaskContext.groovy:198) + at nextflow.cache.CacheDB.writeTaskEntry0(CacheDB.groovy:148) + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) + at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) + at java.base/java.lang.reflect.Method.invoke(Method.java:569) + at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:343) + at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:328) + at groovy.lang.MetaClassImpl.doInvokeMethod(MetaClassImpl.java:1333) + at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1088) + at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1007) + at org.codehaus.groovy.runtime.InvokerHelper.invokePogoMethod(InvokerHelper.java:645) + at org.codehaus.groovy.runtime.InvokerHelper.invokeMethod(InvokerHelper.java:628) + at org.codehaus.groovy.runtime.InvokerHelper.invokeMethodSafe(InvokerHelper.java:82) + at nextflow.cache.CacheDB$_putTaskAsync_closure1.doCall(CacheDB.groovy:157) + at nextflow.cache.CacheDB$_putTaskAsync_closure1.call(CacheDB.groovy) + at groovyx.gpars.agent.AgentBase.onMessage(AgentBase.java:102) + at groovyx.gpars.agent.Agent.handleMessage(Agent.java:84) + at groovyx.gpars.agent.AgentCore$1.handleMessage(AgentCore.java:48) + at groovyx.gpars.util.AsyncMessagingCore.run(AsyncMessagingCore.java:132) + at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136) + at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635) + at java.base/java.lang.Thread.run(Thread.java:840) +Caused by: java.lang.IllegalArgumentException: Unable to create serializer "com.esotericsoftware.kryo.serializers.FieldSerializer" for class: java.lang.ref.ReferenceQueue + at com.esotericsoftware.kryo.factories.ReflectionSerializerFactory.makeSerializer(ReflectionSerializerFactory.java:48) + at com.esotericsoftware.kryo.factories.ReflectionSerializerFactory.makeSerializer(ReflectionSerializerFactory.java:26) + at com.esotericsoftware.kryo.Kryo.newDefaultSerializer(Kryo.java:351) + at com.esotericsoftware.kryo.Kryo.getDefaultSerializer(Kryo.java:344) + at com.esotericsoftware.kryo.util.DefaultClassResolver.registerImplicit(DefaultClassResolver.java:56) + at com.esotericsoftware.kryo.Kryo.getRegistration(Kryo.java:461) + at com.esotericsoftware.kryo.util.DefaultClassResolver.writeClass(DefaultClassResolver.java:79) + at com.esotericsoftware.kryo.Kryo.writeClass(Kryo.java:488) + at com.esotericsoftware.kryo.serializers.ObjectField.write(ObjectField.java:57) + ... 106 common frames omitted +Caused by: java.lang.reflect.InvocationTargetException: null + at jdk.internal.reflect.GeneratedConstructorAccessor39.newInstance(Unknown Source) + at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) + at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:500) + at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:481) + at com.esotericsoftware.kryo.factories.ReflectionSerializerFactory.makeSerializer(ReflectionSerializerFactory.java:35) + ... 114 common frames omitted +Caused by: java.lang.reflect.InaccessibleObjectException: Unable to make field private final java.lang.ref.ReferenceQueue$Lock java.lang.ref.ReferenceQueue.lock accessible: module java.base does not "opens java.lang.ref" to unnamed module @7b02881e + at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:354) + at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:297) + at java.base/java.lang.reflect.Field.checkCanSetAccessible(Field.java:178) + at java.base/java.lang.reflect.Field.setAccessible(Field.java:172) + at com.esotericsoftware.kryo.serializers.FieldSerializer.buildValidFields(FieldSerializer.java:282) + at com.esotericsoftware.kryo.serializers.FieldSerializer.rebuildCachedFields(FieldSerializer.java:217) + at com.esotericsoftware.kryo.serializers.FieldSerializer.rebuildCachedFields(FieldSerializer.java:156) + at com.esotericsoftware.kryo.serializers.FieldSerializer.(FieldSerializer.java:133) + ... 119 common frames omitted +May-07 23:02:17.570 [Task monitor] DEBUG n.processor.TaskPollingMonitor - Task completed > TaskHandler[id: 5; name: NFCORE_DMSCORE:DMSCORE:BWA_MEM (gid1a_1_quality_1_pe); status: COMPLETED; exit: 0; error: -; workDir: /Users/benjaminwehnert/dmscore/work/67/c2a06b1c7b4781da339f3e7aacd574] +May-07 23:02:17.585 [Task submitter] DEBUG n.executor.local.LocalTaskHandler - Launch cmd line: /bin/bash -ue .command.run +May-07 23:02:17.586 [Task submitter] INFO nextflow.Session - [48/c69654] Submitted process > NFCORE_DMSCORE:DMSCORE:FASTQC (gid1a_1_quality_1_pe) +May-07 23:02:17.748 [TaskFinalizer-4] DEBUG nextflow.processor.TaskProcessor - Process NFCORE_DMSCORE:DMSCORE:BWA_MEM > Skipping output binding because one or more optional files are missing: fileoutparam<1:1> +May-07 23:02:17.749 [TaskFinalizer-4] DEBUG nextflow.processor.TaskProcessor - Process NFCORE_DMSCORE:DMSCORE:BWA_MEM > Skipping output binding because one or more optional files are missing: fileoutparam<2:1> +May-07 23:02:17.749 [TaskFinalizer-4] DEBUG nextflow.processor.TaskProcessor - Process NFCORE_DMSCORE:DMSCORE:BWA_MEM > Skipping output binding because one or more optional files are missing: fileoutparam<3:1> +May-07 23:02:17.828 [Actor Thread 14] WARN nextflow.processor.TaskContext - Cannot serialize context map. Cause: java.lang.IllegalArgumentException: Unable to create serializer "com.esotericsoftware.kryo.serializers.FieldSerializer" for class: java.lang.ref.ReferenceQueue -- Resume will not work on this process +May-07 23:02:28.533 [Task monitor] DEBUG n.processor.TaskPollingMonitor - Task completed > TaskHandler[id: 4; name: NFCORE_DMSCORE:DMSCORE:FASTQC (gid1a_1_quality_1_pe); status: COMPLETED; exit: 0; error: -; workDir: /Users/benjaminwehnert/dmscore/work/48/c69654e9b29a194ea81b7c28f63819] +May-07 23:02:28.582 [Task submitter] DEBUG n.executor.local.LocalTaskHandler - Launch cmd line: /bin/bash -ue .command.run +May-07 23:02:28.583 [Task submitter] INFO nextflow.Session - [4e/7ee9bd] Submitted process > NFCORE_DMSCORE:DMSCORE:BAMFILTER_DMS (gid1a_1_quality_1_pe) +May-07 23:02:28.752 [Actor Thread 12] DEBUG nextflow.sort.BigSort - Sort completed -- entries: 2; slices: 1; internal sort time: 0.013 s; external sort time: 0.008 s; total time: 0.021 s +May-07 23:02:28.776 [Actor Thread 12] DEBUG nextflow.file.FileCollector - Saved collect-files list to: /Users/benjaminwehnert/dmscore/work/collect-file/c1149f4a215bbe0651400d7f32cdafdc +May-07 23:02:28.784 [Actor Thread 12] DEBUG nextflow.file.FileCollector - Deleting file collector temp dir: /var/folders/r0/ldrzd4wn1s3516hy0vsn8xzm0000gn/T/nxf-15345989576056151242 +May-07 23:02:32.866 [Task monitor] DEBUG n.processor.TaskPollingMonitor - Task completed > TaskHandler[id: 6; name: NFCORE_DMSCORE:DMSCORE:BAMFILTER_DMS (gid1a_1_quality_1_pe); status: COMPLETED; exit: 0; error: -; workDir: /Users/benjaminwehnert/dmscore/work/4e/7ee9bd66291d03ed6f3c2bbdfefe0d] +May-07 23:02:32.872 [Task submitter] DEBUG n.executor.local.LocalTaskHandler - Launch cmd line: /bin/bash -ue .command.run +May-07 23:02:32.873 [Task submitter] INFO nextflow.Session - [55/5dd043] Submitted process > NFCORE_DMSCORE:DMSCORE:MULTIQC +May-07 23:02:43.262 [Task monitor] DEBUG n.processor.TaskPollingMonitor - Task completed > TaskHandler[id: 7; name: NFCORE_DMSCORE:DMSCORE:MULTIQC; status: COMPLETED; exit: 0; error: -; workDir: /Users/benjaminwehnert/dmscore/work/55/5dd0430083da5c332e7950544ac6e0] +May-07 23:02:43.292 [Task submitter] DEBUG n.executor.local.LocalTaskHandler - Launch cmd line: /bin/bash -ue .command.run +May-07 23:02:43.294 [Task submitter] INFO nextflow.Session - [aa/6e83ae] Submitted process > NFCORE_DMSCORE:DMSCORE:PREMERGE (gid1a_1_quality_1_pe) +May-07 23:02:43.298 [TaskFinalizer-7] DEBUG nextflow.processor.TaskProcessor - Process NFCORE_DMSCORE:DMSCORE:MULTIQC > Skipping output binding because one or more optional files are missing: fileoutparam<2> +May-07 23:02:46.384 [Task monitor] DEBUG n.processor.TaskPollingMonitor - Task completed > TaskHandler[id: 8; name: NFCORE_DMSCORE:DMSCORE:PREMERGE (gid1a_1_quality_1_pe); status: COMPLETED; exit: 0; error: -; workDir: /Users/benjaminwehnert/dmscore/work/aa/6e83aebc1b031afabb4d8df09e97f3] +May-07 23:02:46.488 [Task submitter] DEBUG n.executor.local.LocalTaskHandler - Launch cmd line: /bin/bash -ue .command.run +May-07 23:02:46.489 [Task submitter] INFO nextflow.Session - [f2/f878d3] Submitted process > NFCORE_DMSCORE:DMSCORE:GATK_SATURATIONMUTAGENESIS (gid1a_1_quality_1_pe) +May-07 23:03:05.753 [Task monitor] DEBUG n.processor.TaskPollingMonitor - Task completed > TaskHandler[id: 9; name: NFCORE_DMSCORE:DMSCORE:GATK_SATURATIONMUTAGENESIS (gid1a_1_quality_1_pe); status: COMPLETED; exit: 0; error: -; workDir: /Users/benjaminwehnert/dmscore/work/f2/f878d301d101b20d245c27be19bbf3] +May-07 23:03:05.837 [Actor Thread 2] DEBUG nextflow.processor.TaskProcessor - Handling unexpected condition for + task: name=NFCORE_DMSCORE:DMSCORE:DMSANALYSIS_PROCESS_GATK (1); work-dir=null + error [nextflow.exception.ProcessUnrecoverableException]: Path value cannot be null +May-07 23:03:05.858 [Actor Thread 2] ERROR nextflow.processor.TaskProcessor - Error executing process > 'NFCORE_DMSCORE:DMSCORE:DMSANALYSIS_PROCESS_GATK (1)' + +Caused by: + Path value cannot be null + + + +Tip: you can replicate the issue by changing to the process work dir and entering the command `bash .command.run` +May-07 23:03:05.860 [Actor Thread 2] INFO nextflow.Session - Execution cancelled -- Finishing pending tasks before exit +May-07 23:03:05.865 [Actor Thread 2] ERROR nextflow.Nextflow - Pipeline failed. Please refer to troubleshooting docs: https://nf-co.re/docs/usage/troubleshooting +May-07 23:03:05.866 [main] DEBUG nextflow.Session - Session await > all processes finished +May-07 23:03:05.867 [Task monitor] DEBUG n.processor.TaskPollingMonitor - <<< barrier arrives (monitor: local) - terminating tasks monitor poll loop +May-07 23:03:05.867 [main] DEBUG nextflow.Session - Session await > all barriers passed +May-07 23:03:05.868 [Actor Thread 12] DEBUG nextflow.processor.TaskProcessor - Handling unexpected condition for + task: name=NFCORE_DMSCORE:DMSCORE:DMSANALYSIS_PROCESS_GATK; work-dir=null + error [java.lang.InterruptedException]: java.lang.InterruptedException +May-07 23:03:05.872 [main] DEBUG nextflow.util.ThreadPoolManager - Thread pool 'TaskFinalizer' shutdown completed (hard=false) +May-07 23:03:05.872 [main] DEBUG nextflow.util.ThreadPoolManager - Thread pool 'PublishDir' shutdown completed (hard=false) +May-07 23:03:05.880 [main] INFO nextflow.Nextflow - -[nf-core/dmscore] Pipeline completed with errors- +May-07 23:03:05.897 [main] DEBUG n.trace.WorkflowStatsObserver - Workflow completed > WorkflowStats[succeededCount=9; failedCount=0; ignoredCount=0; cachedCount=0; pendingCount=0; submittedCount=0; runningCount=0; retriesCount=0; abortedCount=0; succeedDuration=2m 24s; failedDuration=0ms; cachedDuration=0ms;loadCpus=0; loadMemory=0; peakRunning=2; peakCpus=8; peakMemory=16 GB; ] +May-07 23:03:05.898 [main] DEBUG nextflow.trace.TraceFileObserver - Workflow completed -- saving trace file +May-07 23:03:05.900 [main] DEBUG nextflow.trace.ReportObserver - Workflow completed -- rendering execution report +May-07 23:03:07.314 [main] DEBUG nextflow.trace.TimelineObserver - Workflow completed -- rendering execution timeline +May-07 23:03:07.511 [main] DEBUG nextflow.cache.CacheDB - Closing CacheDB done +May-07 23:03:07.545 [main] INFO org.pf4j.AbstractPluginManager - Stop plugin 'nf-schema@2.3.0' +May-07 23:03:07.545 [main] DEBUG nextflow.plugin.BasePlugin - Plugin stopped nf-schema +May-07 23:03:07.546 [main] DEBUG nextflow.util.ThreadPoolManager - Thread pool 'FileTransfer' shutdown completed (hard=false) +May-07 23:03:07.547 [main] DEBUG nextflow.script.ScriptRunner - > Execution complete -- Goodbye diff --git a/main.nf b/main.nf index 7c028f7..41a451c 100644 --- a/main.nf +++ b/main.nf @@ -26,9 +26,6 @@ include { getGenomeAttribute } from './subworkflows/local/utils_nfcore_deep ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ -// TODO nf-core: Remove this line if you don't need a FASTA file -// This is an example of how to use getGenomeAttribute() to fetch parameters -// from igenomes.config using `--genome` params.fasta = getGenomeAttribute('fasta') /* @@ -60,6 +57,7 @@ workflow NFCORE_DEEPMUTSCAN { emit: multiqc_report = DEEPMUTSCAN.out.multiqc_report // channel: /path/to/multiqc_report.html } + /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ RUN MAIN WORKFLOW @@ -78,10 +76,7 @@ workflow { params.monochrome_logs, args, params.outdir, - params.input, - params.help, - params.help_full, - params.show_hidden + params.input ) // diff --git a/modules.json b/modules.json index adda4fc..d9f1096 100644 --- a/modules.json +++ b/modules.json @@ -5,6 +5,16 @@ "https://github.com/nf-core/modules.git": { "modules": { "nf-core": { + "bwa/index": { + "branch": "master", + "git_sha": "6d46786420b4d7bc88eba026eb389c0c5535d120", + "installed_by": ["modules"] + }, + "bwa/mem": { + "branch": "master", + "git_sha": "2fb127c8fd13de0adaa676df7169131e45c0b114", + "installed_by": ["modules"] + }, "fastqc": { "branch": "master", "git_sha": "6d46786420b4d7bc88eba026eb389c0c5535d120", @@ -12,7 +22,17 @@ }, "multiqc": { "branch": "master", - "git_sha": "008f9d3e61209bf995edac3ba531f54e269e1215", + "git_sha": "98403d15b0e50edae1f3fec5eae5e24982f1fade", + "installed_by": ["modules"] + }, + "samtools/index": { + "branch": "master", + "git_sha": "6d46786420b4d7bc88eba026eb389c0c5535d120", + "installed_by": ["modules"] + }, + "samtools/sort": { + "branch": "master", + "git_sha": "6d46786420b4d7bc88eba026eb389c0c5535d120", "installed_by": ["modules"] } } @@ -21,7 +41,7 @@ "nf-core": { "utils_nextflow_pipeline": { "branch": "master", - "git_sha": "05954dab2ff481bcb999f24455da29a5828af08d", + "git_sha": "1a545fcbd762911c21a64ced3dbef99b2b51ac75", "installed_by": ["subworkflows"] }, "utils_nfcore_pipeline": { @@ -31,7 +51,7 @@ }, "utils_nfschema_plugin": { "branch": "master", - "git_sha": "fdc08b8b1ae74f56686ce21f7ea11ad11990ce57", + "git_sha": "a7b27fd25bfa8dcc07d299e88bd790585901a436", "installed_by": ["subworkflows"] } } diff --git a/modules/local/bamprocessing/bam_filter/environment.yml b/modules/local/bamprocessing/bam_filter/environment.yml new file mode 100644 index 0000000..a5338f6 --- /dev/null +++ b/modules/local/bamprocessing/bam_filter/environment.yml @@ -0,0 +1,5 @@ +channels: + - conda-forge + - bioconda +dependencies: + - bioconda::samtools=1.21 diff --git a/modules/local/bamprocessing/bam_filter/main.nf b/modules/local/bamprocessing/bam_filter/main.nf new file mode 100644 index 0000000..bed65dd --- /dev/null +++ b/modules/local/bamprocessing/bam_filter/main.nf @@ -0,0 +1,51 @@ + +process BAMFILTER_DMS { + tag "$meta.id" + label 'process_single' + + conda "${moduleDir}/environment.yml" + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/samtools:1.21--h96c455f_1': + 'biocontainers/samtools:1.21--h96c455f_1' }" + + input: + tuple val(meta), path(bam) + + output: + tuple val(meta), path("*.bam"), emit: bam + path "versions.yml" , emit: versions + + when: + task.ext.when == null || task.ext.when + + script: + def args = task.ext.args ?: '' + def prefix = task.ext.prefix ?: "${meta.id}" + """ + # Keep primary, mapped, MAPQ>=30 reads and drop indel/splice alignments. + # Wildtype (NM:i:0) reads are intentionally retained (needed for downstream + # sequencing-error correction); the variant counter ignores them for calling. + samtools view -h -F 4 -F 256 -q 30 $bam | \ + samtools view -h | \ + awk '{if(\$6 !~ /I/ && \$6 !~ /D/ && \$6 !~ /N/) print \$0}' | \ + samtools view -bS > ${meta.id}_filtered.bam + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + samtools: \$(samtools --version |& sed '1!d ; s/samtools //') +END_VERSIONS + """ + + stub: + """ + # Must match the name the script block writes: the input bam is staged under + # \${meta.id}.bam, so touching that name here would just re-touch the input and + # leave the process with no output of its own. + touch ${meta.id}_filtered.bam + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + samtools: \$(samtools --version |& sed '1!d ; s/samtools //') +END_VERSIONS + """ +} diff --git a/modules/local/bamprocessing/bam_filter/meta.yml b/modules/local/bamprocessing/bam_filter/meta.yml new file mode 100644 index 0000000..78c92ea --- /dev/null +++ b/modules/local/bamprocessing/bam_filter/meta.yml @@ -0,0 +1,48 @@ +name: "bamfilter_dms" +description: Filters BAM files specifically for Deep Mutational Scanning (DMS) analysis by removing unmapped reads, low-quality alignments, secondary alignments, indels (CIGAR I/D/N), and perfectly matching wild-type reads. +keywords: + - dms + - bam + - filtering + - samtools + - indels +tools: + - "samtools": + description: "Tools for manipulating next-generation sequencing data" + homepage: "http://www.htslib.org/" + documentation: "http://www.htslib.org/doc/samtools.html" + tool_dev_url: "https://github.com/samtools/samtools" + doi: "10.1093/bioinformatics/btp352" + licence: ["MIT/Expat"] + +input: + - - meta: + type: map + description: | + Groovy Map containing sample information + e.g. `[ id:'test', single_end:false ]` + - bam: + type: file + description: Input BAM file to be filtered + pattern: "*.{bam}" + +output: + - bam: + - meta: + type: map + description: | + Groovy Map containing sample information + e.g. `[ id:'test', single_end:false ]` + - "*.bam": + type: file + description: Filtered BAM file containing only high-quality mutated reads without indels + pattern: "*_filtered.bam" + - versions: + - versions.yml: + type: file + description: File containing software versions + pattern: "versions.yml" + +authors: + - "@BenjaminWehnert1008" + - "@MaximilianStammnitz" diff --git a/modules/local/bamprocessing/premerge/environment.yml b/modules/local/bamprocessing/premerge/environment.yml new file mode 100644 index 0000000..0e199ab --- /dev/null +++ b/modules/local/bamprocessing/premerge/environment.yml @@ -0,0 +1,7 @@ +channels: + - conda-forge + - bioconda +dependencies: + - bioconda::bwa=0.7.19 + - bioconda::samtools=1.21 + - bioconda::vsearch=2.30.0 diff --git a/modules/local/bamprocessing/premerge/main.nf b/modules/local/bamprocessing/premerge/main.nf new file mode 100644 index 0000000..fbf433f --- /dev/null +++ b/modules/local/bamprocessing/premerge/main.nf @@ -0,0 +1,51 @@ + +process PREMERGE { + tag "$meta.id" + label 'process_medium' + + conda "${moduleDir}/environment.yml" + container "community.wave.seqera.io/library/bwa_samtools_vsearch:28e8640725d3d8e9" + + input: + tuple val(meta), path(bam) + path wt_seq + + output: + tuple val(meta), path("*.bam"), emit: bam + path "versions.yml", emit: versions + + when: + task.ext.when == null || task.ext.when + + script: + def prefix = task.ext.prefix ?: "${meta.id}" + """ + # Convert BAM to paired FASTQ files + samtools fastq -1 forward_reads.fastq -2 reverse_reads.fastq -0 /dev/null -s /dev/null -n $bam + + # Merge paired reads + vsearch --fastq_mergepairs forward_reads.fastq --reverse reverse_reads.fastq --fastqout merged_reads.fastq --fastq_minovlen 10 --fastq_allowmergestagger + + # Re-align merged reads + bwa index $wt_seq + bwa mem $wt_seq merged_reads.fastq | samtools view -Sb - > ${prefix}_merged.bam + + # Save version information + cat <<-END_VERSIONS > versions.yml + "${task.process}": + premerge: \$(samtools --version |& sed '1!d ; s/samtools //') +END_VERSIONS + """ + + stub: + def prefix = task.ext.prefix ?: "${meta.id}" + """ + touch merged_reads.fastq + touch merged_reads.bam + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + premerge: dummy_version +END_VERSIONS + """ +} diff --git a/modules/local/bamprocessing/premerge/meta.yml b/modules/local/bamprocessing/premerge/meta.yml new file mode 100644 index 0000000..76186fe --- /dev/null +++ b/modules/local/bamprocessing/premerge/meta.yml @@ -0,0 +1,65 @@ +name: "premerge" +description: Processes paired-end BAM files by converting them to FASTQ, merging overlapping paired reads using VSEARCH, and re-aligning the merged single-end reads to the wild-type reference using BWA. +keywords: + - paired-end merging + - alignment + - bwa + - vsearch + - samtools + - dms +tools: + - "samtools": + description: "Tools for manipulating next-generation sequencing data" + homepage: "http://www.htslib.org/" + documentation: "http://www.htslib.org/doc/samtools.html" + tool_dev_url: "https://github.com/samtools/samtools" + doi: "10.1093/bioinformatics/btp352" + licence: ["MIT/Expat"] + - "vsearch": + description: "Versatile open-source tool for metagenomics" + homepage: "https://github.com/torognes/vsearch" + documentation: "https://github.com/torognes/vsearch/blob/master/README.md" + doi: "10.7717/peerj.2584" + licence: ["GPL-3.0-or-later", "BSD-2-Clause"] + - "bwa": + description: "Burrow-Wheeler Aligner for short-read alignment" + homepage: "http://bio-bwa.sourceforge.net/" + documentation: "http://bio-bwa.sourceforge.net/bwa.shtml" + doi: "10.1093/bioinformatics/btp324" + licence: ["GPL-3.0-or-later"] + +input: + - - meta: + type: map + description: | + Groovy Map containing sample information + e.g. `[ id:'test', single_end:false ]` + - bam: + type: file + description: Input paired-end BAM file + pattern: "*.{bam}" + - - wt_seq: + type: file + description: FASTA file containing the wild-type reference sequence + pattern: "*.{fasta,fa}" + +output: + - bam: + - meta: + type: map + description: | + Groovy Map containing sample information + e.g. `[ id:'test', single_end:false ]` + - "*.bam": + type: file + description: BAM file containing the merged and re-aligned reads + pattern: "*_merged.bam" + - versions: + - versions.yml: + type: file + description: File containing software versions + pattern: "versions.yml" + +authors: + - "@BenjaminWehnert1008" + - "@MaximilianStammnitz" diff --git a/modules/local/dmsanalysis/aa_seq/environment.yml b/modules/local/dmsanalysis/aa_seq/environment.yml new file mode 100644 index 0000000..197283a --- /dev/null +++ b/modules/local/dmsanalysis/aa_seq/environment.yml @@ -0,0 +1,15 @@ +channels: + - conda-forge + - bioconda +dependencies: + - bioconda::bioconductor-biostrings=2.78.0 + - conda-forge::r-base=4.5.1 + - conda-forge::r-biocmanager=1.30.27 + - conda-forge::r-dplyr=1.1.4 + - conda-forge::r-ggplot2=4.0.2 + - conda-forge::r-reshape2=1.4.4 + - conda-forge::r-scales=1.4.0 + - conda-forge::r-stringr=1.6.0 + - conda-forge::r-tidyr=1.3.1 + - conda-forge::r-tidyverse=2.0.0 + - conda-forge::r-zoo=1.8_15 diff --git a/modules/local/dmsanalysis/aa_seq/main.nf b/modules/local/dmsanalysis/aa_seq/main.nf new file mode 100644 index 0000000..01ac974 --- /dev/null +++ b/modules/local/dmsanalysis/aa_seq/main.nf @@ -0,0 +1,31 @@ +process DMSANALYSIS_AASEQ { + tag "amino_acid_sequence" + label 'process_single' + + conda "${moduleDir}/environment.yml" + + container "${ workflow.containerEngine == 'singularity' + ? 'https://community-cr-prod.seqera.io/docker/registry/v2/blobs/sha256/73/73a72ec77725aeb67678a74228938fdd6827b669d01a8c96951b1a8ef96eeb0f/data' + : 'community.wave.seqera.io/library/bioconductor-biostrings_bioconductor-mutscan_r-base_r-biocmanager_pruned:c65036d76406f342' }" + + input: + tuple val(meta), path(wt_seq) + val pos_range + + output: + tuple val(meta), path("aa_seq.txt"), emit: aa_seq + path "versions.yml", emit: versions + + when: + task.ext.when == null || task.ext.when + + script: + template 'aa_seq.R' + + stub: + """ + touch aa_seq.txt + echo "DMSANALYSIS_AASEQ:" > versions.yml + echo " stub-version: 0.0.0" >> versions.yml + """ +} diff --git a/modules/local/dmsanalysis/aa_seq/meta.yml b/modules/local/dmsanalysis/aa_seq/meta.yml new file mode 100644 index 0000000..46a761d --- /dev/null +++ b/modules/local/dmsanalysis/aa_seq/meta.yml @@ -0,0 +1,54 @@ +name: "dmsanalysis_aaseq" +description: Translates a wild-type DNA sequence into an amino acid sequence based on a provided coding region (ORF) range. +keywords: + - translation + - amino acid + - dna + - dms + - biostrings +tools: + - "bioconductor-biostrings": + description: "Memory efficient string containers, string matching algorithms, and other utilities, for fast manipulation of large biological sequences or sets of sequences" + homepage: "https://bioconductor.org/packages/Biostrings" + documentation: "https://bioconductor.org/packages/release/bioc/manuals/Biostrings/man/Biostrings.pdf" + licence: ["Artistic-2.0"] + - "mutscan": + description: "R package for analysis of deep mutational scanning data" + homepage: "https://bioconductor.org/packages/mutscan" + tool_dev_url: "https://github.com/csoneson/mutscan" + doi: "10.1186/s12859-023-05187-y" + licence: ["GPL-3.0-or-later"] + +input: + - - meta: + type: map + description: | + Groovy Map containing sample information + e.g. `[ id:'ref' ]` + - wt_seq: + type: file + description: FASTA file containing the wild-type DNA reference sequence + pattern: "*.{fasta,fa}" + - - pos_range: + type: string + description: Start and stop codon positions in the format 'start-stop', e.g., '352-1383' + +output: + - aa_seq: + - meta: + type: map + description: | + Groovy Map containing sample information + - aa_seq.txt: + type: file + description: Text file containing the translated amino acid sequence + pattern: "aa_seq.txt" + - versions: + - versions.yml: + type: file + description: File containing software versions + pattern: "versions.yml" + +authors: + - "@BenjaminWehnert1008" + - "@MaximilianStammnitz" diff --git a/modules/local/dmsanalysis/aa_seq/templates/aa_seq.R b/modules/local/dmsanalysis/aa_seq/templates/aa_seq.R new file mode 100644 index 0000000..7c2e8e5 --- /dev/null +++ b/modules/local/dmsanalysis/aa_seq/templates/aa_seq.R @@ -0,0 +1,65 @@ +#!/usr/bin/env Rscript + +# input: wildtype-seq, start&stopp pos. +# output: amino acid sequence within the start-stop frame (.txt) + +# Load necessary libraries +suppressMessages(library(Biostrings)) + +# Define the function +aa_seq <- function(wt_seq_input, pos_range, output_file) { + # Parse the start and stop positions from the input format "start-stop" + positions <- unlist(strsplit(pos_range, "-")) + start_pos <- as.numeric(positions[1]) + stop_pos <- as.numeric(positions[2]) + + # Check if the input is a file or a string + if (file.exists(wt_seq_input)) { + # If it's a file, read the sequence from the fasta file + seq_data <- readDNAStringSet(filepath = wt_seq_input) + wt_seq <- seq_data[[1]] # Extract the sequence + } else { + # Otherwise, treat the input as a sequence string + wt_seq <- DNAString(wt_seq_input) + } + + # Extract the sequence between start and stop codons + coding_seq <- subseq(wt_seq, start = start_pos, end = stop_pos) + + # Translate the coding sequence into an amino acid sequence + aa_seq <- translate(coding_seq) + + # Write the amino acid sequence to a .txt file + write(as.character(aa_seq), file = output_file) +} + + +##### +# run function +##### +aa_seq( + wt_seq_input = "$wt_seq", + "$pos_range", + "aa_seq.txt" + ) + + +#### +# create versions.yml +#### +r_version <- strsplit(version[['version.string']], ' ')[[1]][3] +Biostrings_version <- as.character(packageVersion("Biostrings")) + +if (is.null(r_version)) r_version <- "unknown" +if (length(Biostrings_version) == 0) Biostrings_version <- "unknown" + +f <- file("versions.yml", "w") +writeLines( + c( + '"${task.process}":', + paste(' r-base:', r_version), + paste(' r-Biostrings:', Biostrings_version) + ), + f +) +close(f) diff --git a/modules/local/dmsanalysis/error_correction_false_doubles/environment.yml b/modules/local/dmsanalysis/error_correction_false_doubles/environment.yml new file mode 100644 index 0000000..07bd598 --- /dev/null +++ b/modules/local/dmsanalysis/error_correction_false_doubles/environment.yml @@ -0,0 +1,7 @@ +channels: + - conda-forge + - bioconda +dependencies: + - conda-forge::r-base=4.5.1 + - conda-forge::r-stringr=1.6.0 + - bioconda::bioconductor-biostrings=2.78.0 diff --git a/modules/local/dmsanalysis/error_correction_false_doubles/main.nf b/modules/local/dmsanalysis/error_correction_false_doubles/main.nf new file mode 100644 index 0000000..c43bd23 --- /dev/null +++ b/modules/local/dmsanalysis/error_correction_false_doubles/main.nf @@ -0,0 +1,43 @@ +process DMSANALYSIS_ERROR_CORRECTION_FALSE_DOUBLES { + tag "$meta.id" + label 'process_single' + + conda "${moduleDir}/environment.yml" + + container "${ workflow.containerEngine == 'singularity' + ? 'https://community-cr-prod.seqera.io/docker/registry/v2/blobs/sha256/73/73a72ec77725aeb67678a74228938fdd6827b669d01a8c96951b1a8ef96eeb0f/data' + : 'community.wave.seqera.io/library/bioconductor-biostrings_bioconductor-mutscan_r-base_r-biocmanager_pruned:c65036d76406f342' }" + + input: + tuple val(meta), path(raw_counts), path(filtered), path(completed) + path wt_fasta + path aa_seq + val min_counts + val method + val codon_window + + output: + tuple val(meta), + path("variantCounts_filtered_by_library_error_corrected.csv"), + path("variantCounts_for_heatmaps_error_corrected.csv"), + path("library_completed_variantCounts_error_corrected.csv"), + emit: corrected + tuple val(meta), path("seq_error_rate.csv"), emit: seq_error_rate + path "versions.yml", emit: versions + + when: + task.ext.when == null || task.ext.when + + script: + template 'error_correction_false_doubles.R' + + stub: + """ + touch variantCounts_filtered_by_library_error_corrected.csv + touch variantCounts_for_heatmaps_error_corrected.csv + touch library_completed_variantCounts_error_corrected.csv + touch seq_error_rate.csv + echo "DMSANALYSIS_ERROR_CORRECTION_FALSE_DOUBLES:" > versions.yml + echo " stub-version: 0.0.0" >> versions.yml + """ +} diff --git a/modules/local/dmsanalysis/error_correction_false_doubles/meta.yml b/modules/local/dmsanalysis/error_correction_false_doubles/meta.yml new file mode 100644 index 0000000..5840a90 --- /dev/null +++ b/modules/local/dmsanalysis/error_correction_false_doubles/meta.yml @@ -0,0 +1,66 @@ +name: "dmsanalysis_error_correction_false_doubles" +description: "Sequencing-error correction of single-codon variant counts using the false-double-mutant model: because the library only contains single-codon changes, observed multi-codon variants are treated as errors and used to estimate and subtract the per-nucleotide error rate." +keywords: + - deep mutational scanning + - error correction + - false doubles + - variant counting +tools: + - "r-base": + description: "The R programming language (base only)" + homepage: "https://www.r-project.org/" + licence: ["GPL-2.0-or-later"] + +input: + - - meta: + type: map + description: | + Groovy Map containing sample information + e.g. `[ id:'test', sample:'ORF1', type:'input', replicate:1 ]` + - raw_counts: + type: file + description: Raw (pre-library-filter) variant count table from VARIANTCOUNTING. + pattern: "*.variant_counts.tsv" + - filtered: + type: file + description: Library-filtered single-codon variant counts (annotated CSV). + pattern: "*.csv" + - completed: + type: file + description: Completed library table (all possible variants) to also correct. + pattern: "*.csv" + - - aa_seq: + type: file + description: Reference amino-acid sequence (one line). + - - min_counts: + type: integer + description: Minimum count threshold for the amino-acid-level heatmap table. + +output: + - corrected: + - meta: + type: map + description: Groovy Map containing sample information + - variantCounts_filtered_by_library_error_corrected.csv: + type: file + description: Library-filtered counts with added `counts_corrected` / `counts_per_cov_corrected` columns. + pattern: "variantCounts_filtered_by_library_error_corrected.csv" + - variantCounts_for_heatmaps_error_corrected.csv: + type: file + description: Amino-acid-level heatmap table built from corrected counts (canonical `total_counts` columns). + pattern: "variantCounts_for_heatmaps_error_corrected.csv" + - library_completed_variantCounts_error_corrected.csv: + type: file + description: Completed library table with corrected counts propagated (for logdiff etc.). + pattern: "library_completed_variantCounts_error_corrected.csv" + - versions: + - versions.yml: + type: file + description: File containing software versions + pattern: "versions.yml" + +authors: + - "@BenjaminWehnert1008" + - "@MaximilianStammnitz" +maintainers: + - "@BenjaminWehnert1008" diff --git a/modules/local/dmsanalysis/error_correction_false_doubles/templates/error_correction_false_doubles.R b/modules/local/dmsanalysis/error_correction_false_doubles/templates/error_correction_false_doubles.R new file mode 100644 index 0000000..9282ca3 --- /dev/null +++ b/modules/local/dmsanalysis/error_correction_false_doubles/templates/error_correction_false_doubles.R @@ -0,0 +1,741 @@ +#!/usr/bin/env Rscript + +## false-double mutant codon sequencing-error correction in nf-core/deepmutscan (nucleotide level). +## +## Two estimators, ported VERBATIM from Maxi's scripts (24.06.2026): +## * MLE (default): per-SNV maximum-likelihood error rate e = F / m, applied per variant. +## * EB: pool F and m across SNVs, then a Gamma-Poisson empirical-Bayes fit with +## six-category shrinkage -> posterior-mean rate e_EB. +## `$method` (mle|eb) selects which one runs. Everything except I/O plumbing and the dispatch is +## kept exactly as written. +## +## Nextflow template: an R `\$` is a literal dollar; backslashes meant for R are doubled. + +suppressMessages({ + library(Biostrings) + library(stringr) +}) + +## ------------------------------------------------------------------------------------------------- +## MLE estimator (verbatim) +## ------------------------------------------------------------------------------------------------- +seq_error_correct_by_false_doubles_MLE <- function(wt_path, input_count_path_raw, input_count_path_processed, output_file_path, seq_error_rate_path, codon_window){ + + ## load data (nucleotide-level counts from GATK) + + # WT sequence + wt.seq <- readDNAStringSet(wt_path) + + # for the seq error rates, build a vector to be filled + seq.error.names <- c() + for(i in 1:nchar(wt.seq)){ + tmp.wt <- strsplit(as.character(wt.seq), "")[[1]][i] + seq.error.names <- c(seq.error.names, paste0(i, ":", tmp.wt, ">", c("A", "T", "G", "C"))[-match(tmp.wt, c("A", "T", "G", "C"))]) + } + seq.error.rate <- matrix(0, nrow = length(seq.error.names), ncol = 2) + rownames(seq.error.rate) <- seq.error.names + colnames(seq.error.rate) <- c("1nt counts/coverage", "1nt false counts/coverage") + + # Set the column names + colnames <- c("counts", "cov", "mean_length_variant_reads", "varying_bases", + "base_mut", "varying_codons", "codon_mut", "aa_mut", "pos_mut") + input.counts.raw <- read.table(input_count_path_raw, sep = "\\t", header = FALSE, fill = TRUE, col.names = colnames) + input.counts.processed <- read.csv(input_count_path_processed) + + ## append key columns + input.counts.processed\$counts_corrected <- input.counts.processed\$counts + input.counts.processed\$counts_per_cov_corrected <- input.counts.processed\$counts_per_cov + + ## calculate some things upfront + + ### highest accessible codon + max.codon <- max(as.numeric(str_split_fixed(input.counts.raw\$codon_mut, ":", 2)[,1]), na.rm = T) + + ### total distribution of false double mutant coverage decay (%) vs. codon-codon distance + cat("Estimating the distance-dependent coverage decay of false double mutants...\\n") + all.false.doubles <- input.counts.raw + all.false.doubles <- all.false.doubles[which(all.false.doubles[,"varying_bases"] >= 3 & all.false.doubles[,"varying_bases"] < 5 & all.false.doubles[,"varying_codons"] == 2),,drop = F] + all.false.doubles\$codon_dist <- vapply(regmatches(all.false.doubles[,"codon_mut"], gregexpr("\\\\d+(?=:)", all.false.doubles[,"codon_mut"], perl = TRUE)), function(z) abs(diff(as.integer(z))), integer(1)) + + all.false.doubles.codons <- str_split_fixed(all.false.doubles\$codon_mut, ", ", 2) + all.false.doubles.codons[,1] <- as.integer(sub(":.*", "", all.false.doubles.codons[,1])) + all.false.doubles.codons[,2] <- as.integer(sub(":.*", "", all.false.doubles.codons[,2])) + median.high.conf.coverage.per.pos <- rep(NA, max(as.numeric(str_split_fixed(input.counts.processed\$codon_mut, ":", 2)[,1]))) + names(median.high.conf.coverage.per.pos) <- 1:max(as.numeric(str_split_fixed(input.counts.processed\$codon_mut, ":", 2)[,1])) + for(i in 1:length(median.high.conf.coverage.per.pos)){ + median.high.conf.coverage.per.pos[i] <- median(input.counts.processed[which(names(median.high.conf.coverage.per.pos)[i] == str_split_fixed(input.counts.processed\$codon_mut, ":", 2)[,1]),"cov"]) + } + all.false.doubles.codons[,1] <- median.high.conf.coverage.per.pos[match(all.false.doubles.codons[,1], names(median.high.conf.coverage.per.pos))] + all.false.doubles.codons[,2] <- median.high.conf.coverage.per.pos[match(all.false.doubles.codons[,2], names(median.high.conf.coverage.per.pos))] + all.false.doubles\$max_cov_possible <- as.numeric(apply(all.false.doubles.codons, 1, min)) + all.false.doubles\$perc_cov_to_max <- 100 * all.false.doubles\$cov / all.false.doubles\$max_cov_possible + + ## deepmutscan guard (not in the verbatim script): a run can legitimately contain NO observed false + ## double mutants -- a small, shallow or tightly-filtered library, or the nf-core test dataset, where + ## every variant is single-codon. The verbatim code then hands lm() an empty frame and dies with + ## "0 (non-NA) cases". With no false doubles anywhere there is also no evidence of codon-level + ## sequencing error to subtract, so the correct answer is a no-op: corrected == raw. The quadratic + ## needs >= 3 distinct codon distances to be estimable at all, so require that too. + .usable <- is.finite(log(all.false.doubles\$perc_cov_to_max)) + if (nrow(all.false.doubles) == 0 || length(unique(all.false.doubles\$codon_dist[.usable])) < 3) { + cat("WARNING: only", sum(.usable), "usable false double mutant(s) found - the distance-dependent\\n", + "coverage decay is not estimable, so NO sequencing-error correction is applied and the\\n", + "corrected counts are identical to the raw counts.\\n") + write.csv(seq.error.rate, file = seq_error_rate_path, row.names = T) + write.csv(input.counts.processed, file = output_file_path, row.names = F) + return(invisible(NULL)) + } + + fit <- lm(log(all.false.doubles\$perc_cov_to_max) ~ all.false.doubles\$codon_dist + I(all.false.doubles\$codon_dist^2)) + all.false.doubles\$pred_cov_perc <- exp(predict(fit)) + + ### convert this into a look-up table: "% max. possible coverage" depending on codon-codon distance + dual.codon.coverage.estimate <- matrix(NA, nrow = max(all.false.doubles\$codon_dist), ncol = 2) + colnames(dual.codon.coverage.estimate) <- c("Codon distance", "Estimate % max. possible coverage") + dual.codon.coverage.estimate[,"Codon distance"] <- 1:nrow(dual.codon.coverage.estimate) + ## deepmutscan fix (Maxi-approved deviation from the verbatim script): fill the coverage-decay + ## lookup from the fitted quadratic evaluated at EVERY integer codon distance, instead of match()ing + ## only the distances that happen to have an observed double. `pred_cov_perc = exp(predict(fit))` + ## depends solely on codon_dist, so this is bit-identical to the original at every observed distance + ## and merely fills the previously-NA gaps -- those NAs otherwise crash MLE and leave NA corrected + ## counts in EB (a distance with no observed double, e.g. around codon 288 on the GID1A min_counts=10 + ## data). + .dccd <- dual.codon.coverage.estimate[,"Codon distance"] + .cf <- coef(fit) + dual.codon.coverage.estimate[,"Estimate % max. possible coverage"] <- exp(.cf[1] + .cf[2] * .dccd + .cf[3] * .dccd^2) + + ## Process the GATK file, only look at single nucleotide variants + cat("Sequencing error correction of GATK counts...\\n") + for (i in grep("[,]", input.counts.processed[,"base_mut"], invert = T)){ + + ### algorithm: + ### 1.) isolate 1nt variant of interest + ### 2.) look for 2/3nt hits in a pre-specified window of codons upstream or downstream + ### -> if there are zero false double mutants: skip / set correction factor to zero + ### -> if there are any false double mutants: continue + ### 3.) generate a table for ALL possible 2/3nt variants in the selected window: + ### true high-conf. variant count | true high-conf. variant coverage | false double double variant count (often 0 or 1) | false double double variant coverage + ### 4.) Maximum likelihood estimation (MLE) over all events + ### 5.) Apply correction factor + + ## 1.) isolate 1nt variant of interest + tmp.single <- input.counts.processed[i,"base_mut"] + seq.error.rate[tmp.single, "1nt counts/coverage"] <- input.counts.processed[i,"counts_per_cov"] + + ## 2.) look for 2/3nt hits in up to 40 codons (120 bp) upstream or downstream + tmp.false.doubles <- input.counts.raw[grep(paste0("(?= 3 & tmp.false.doubles[,"varying_bases"] < 5 & tmp.false.doubles[,"varying_codons"] == 2),,drop = F] + if(nrow(tmp.false.doubles) == 0){ + next + } + + ## set and enforce distance threshold + tmp.ref.codon <- as.numeric(strsplit(input.counts.processed[i,"codon_mut"] , ":")[[1]][1]) + tmp.min.from.ref <- max(c(1, tmp.ref.codon - c(codon_window - 1))) + tmp.max.from.ref <- min(c(max.codon, tmp.ref.codon + c(codon_window - 1))) + tmp.double.codon <- str_split_fixed(tmp.false.doubles\$codon_mut, ", ", 2) + tmp.double.codon.n1 <- as.integer(sub(":.*", "", tmp.double.codon[,1])) + tmp.double.codon.n2 <- as.integer(sub(":.*", "", tmp.double.codon[,2])) + tmp.double.codon.within.range <- (tmp.double.codon.n1 >= tmp.min.from.ref & tmp.double.codon.n1 <= tmp.max.from.ref) & (tmp.double.codon.n2 >= tmp.min.from.ref & tmp.double.codon.n2 <= tmp.max.from.ref) + tmp.false.doubles <- tmp.false.doubles[which(tmp.double.codon.within.range == T),,drop = F] + if(nrow(tmp.false.doubles) == 0){ + next + } + + ## 3.) generate a table for ALL possible 2/3nt variants in the selected window + tmp.summary.table.entries <- input.counts.processed + tmp.summary.table.entries <- tmp.summary.table.entries[which(as.numeric(str_split_fixed(tmp.summary.table.entries\$codon_mut, ":", 2)[,1]) != tmp.ref.codon),] + tmp.summary.table.entries <- tmp.summary.table.entries[which(as.numeric(str_split_fixed(tmp.summary.table.entries\$codon_mut, ":", 2)[,1]) >= tmp.min.from.ref & + as.numeric(str_split_fixed(tmp.summary.table.entries\$codon_mut, ":", 2)[,1]) <= tmp.max.from.ref),] + tmp.summary.table.entries <- tmp.summary.table.entries[which(tmp.summary.table.entries\$varying_bases > 1),] + + ### fill the table + tmp.summary.table <- matrix(NA, ncol = 5, nrow = nrow(tmp.summary.table.entries)) + colnames(tmp.summary.table) <- c("codon distance", + "true high-conf. variant count", + "true high-conf. variant coverage", + "false double double variant count", + "false double double variant coverage") + rownames(tmp.summary.table) <- paste0(c(tmp.summary.table.entries\$codon_mut),", ", c(tmp.summary.table.entries\$base_mut)) + + tmp.summary.table[,"codon distance"] <- abs(as.numeric(str_split_fixed(tmp.summary.table.entries\$codon_mut, ":", 2)[,1]) - tmp.ref.codon) + tmp.summary.table[,"true high-conf. variant count"] <- tmp.summary.table.entries\$counts + tmp.summary.table[,"true high-conf. variant coverage"] <- tmp.summary.table.entries\$cov + tmp.summary.table[,"false double double variant count"] <- 0 + + ## input the actual hits + tmp.double.codon <- str_split_fixed(tmp.false.doubles\$codon_mut, ", ", 2) + tmp.match1 <- match(tmp.double.codon[,1], str_split_fixed(rownames(tmp.summary.table), ", ", 2)[,1]) + tmp.match1[is.na(tmp.match1)] <- 0 + tmp.match2 <- match(tmp.double.codon[,2], str_split_fixed(rownames(tmp.summary.table), ", ", 2)[,1]) + tmp.match2[is.na(tmp.match2)] <- 0 + tmp.match <- c(tmp.match1 + tmp.match2) + + ### remove those false doubles for which there are no matched true high confidence calls + if(any(tmp.match == 0)){ + tmp.false.doubles <- tmp.false.doubles[-which(tmp.match == 0),,drop = F] + tmp.match <- tmp.match[-which(tmp.match == 0)] + if(nrow(tmp.false.doubles) == 0){ + next + } + } + + tmp.summary.table[tmp.match,"false double double variant count"] <- tmp.false.doubles[,"counts"] + tmp.summary.table[tmp.match,"false double double variant coverage"] <- tmp.false.doubles[,"cov"] + tmp.summary.table <- as.data.frame(tmp.summary.table) + + ## 4.) Maximum likelihood estimation (MLE) over all events, iterating over codon positions + + ### summarise data by position + tmp.summary.table.pos <- matrix(NA, nrow = length(unique(str_split_fixed(rownames(tmp.summary.table), ">", 2)[,1])), ncol = 5) + colnames(tmp.summary.table.pos) <- colnames(tmp.summary.table) + rownames(tmp.summary.table.pos) <- unique(str_split_fixed(rownames(tmp.summary.table), ">", 2)[,1]) + tmp.summary.table.pos[,"codon distance"] <- abs(as.numeric(str_split_fixed(rownames(tmp.summary.table.pos), ":", 2)[,1]) - as.numeric(str_split_fixed(input.counts.processed[i,"codon_mut"], ":", 2)[,1])) + for(j in 1:nrow(tmp.summary.table.pos)){ + tmp.name <- rownames(tmp.summary.table.pos)[j] + tmp.summary.table.pos[j,"true high-conf. variant count"] <- sum(tmp.summary.table[grep(paste0("^",tmp.name), rownames(tmp.summary.table)),"true high-conf. variant count"]) + tmp.summary.table.pos[j,"true high-conf. variant coverage"] <- max(tmp.summary.table[grep(paste0("^",tmp.name), rownames(tmp.summary.table)),"true high-conf. variant coverage"]) + tmp.summary.table.pos[j,"false double double variant count"] <- sum(tmp.summary.table[grep(paste0("^",tmp.name), rownames(tmp.summary.table)),"false double double variant count"]) + tmp.summary.table.pos[j,"false double double variant coverage"] <- max(tmp.summary.table[grep(paste0("^",tmp.name), rownames(tmp.summary.table)),"false double double variant coverage"], na.rm = T) + + ## infer a good proxy for the false double variant coverage with 0 counts (based estimated distance-dependent % coverage decay observed across all observed double mutants) + if(tmp.summary.table.pos[j,"false double double variant coverage"] == "-Inf"){ + tmp.summary.table.pos[j,"false double double variant coverage"] <- c(dual.codon.coverage.estimate[abs(tmp.summary.table.pos[j,"codon distance"]),"Estimate % max. possible coverage"] / 100) * tmp.summary.table.pos[j,"true high-conf. variant coverage"] + tmp.summary.table.pos[j,"false double double variant coverage"] <- round(tmp.summary.table.pos[j,"false double double variant coverage"]) + } + } + tmp.summary.table.pos <- as.data.frame(tmp.summary.table.pos) + e_MLE <- sum(tmp.summary.table.pos\$`false double double variant count`) / + sum(tmp.summary.table.pos\$`true high-conf. variant count` * c(tmp.summary.table.pos\$`false double double variant coverage` / tmp.summary.table.pos\$`true high-conf. variant coverage`)) + seq.error.rate[tmp.single,"1nt false counts/coverage"] <- e_MLE + input.counts.processed[i,"counts_per_cov_corrected"] <- input.counts.processed[i,"counts_per_cov"] - e_MLE + + ## 5.) Apply correction factor + input.counts.processed[i,"counts_corrected"] <- input.counts.processed[i,"counts"] - c(e_MLE * input.counts.processed[i,"cov"]) + + ## round to nearest integer, do not allow for negative counts + input.counts.processed[i,"counts_corrected"] <- round(input.counts.processed[i,"counts_corrected"]) + if(input.counts.processed[i,"counts_corrected"] < 0){ + input.counts.processed[i,"counts_corrected"] <- 0 + input.counts.processed[i,"counts_per_cov_corrected"] <- 0 + } + + } + + # Write the processed data + write.csv(seq.error.rate, file = seq_error_rate_path, row.names = T) + write.csv(input.counts.processed, file = output_file_path, row.names = F) + +} + +## ------------------------------------------------------------------------------------------------- +## EB estimator (verbatim) +## ------------------------------------------------------------------------------------------------- +seq_error_correct_by_false_doubles_EB <- function(wt_path, input_count_path_raw, input_count_path_processed, output_file_path, seq_error_rate_path, codon_window){ + + ## load data (nucleotide-level counts from GATK) + + # WT sequence + wt.seq <- readDNAStringSet(wt_path) + + # for the seq error rates, build a vector to be filled + seq.error.names <- c() + for(i in 1:nchar(wt.seq)){ + tmp.wt <- strsplit(as.character(wt.seq), "")[[1]][i] + seq.error.names <- c(seq.error.names, paste0(i, ":", tmp.wt, ">", c("A", "T", "G", "C"))[-match(tmp.wt, c("A", "T", "G", "C"))]) + } + seq.error.rate <- matrix(0, nrow = length(seq.error.names), ncol = 2) + rownames(seq.error.rate) <- seq.error.names + colnames(seq.error.rate) <- c("1nt counts/coverage", "1nt false counts/coverage") + + # Set the column names + colnames <- c("counts", "cov", "mean_length_variant_reads", "varying_bases", + "base_mut", "varying_codons", "codon_mut", "aa_mut", "pos_mut") + input.counts.raw <- read.table(input_count_path_raw, sep = "\\t", header = FALSE, fill = TRUE, col.names = colnames) + input.counts.processed <- read.csv(input_count_path_processed) + + ## append key columns + input.counts.processed\$counts_corrected <- input.counts.processed\$counts + input.counts.processed\$counts_per_cov_corrected <- input.counts.processed\$counts_per_cov + + ## calculate some things upfront + + ### highest accessible codon + max.codon <- max(as.numeric(str_split_fixed(input.counts.raw\$codon_mut, ":", 2)[,1]), na.rm = T) + + ### total distribution of false double mutant coverage decay (%) vs. codon-codon distance + cat("Estimating the distance-dependent coverage decay of false double mutants...\\n") + all.false.doubles <- input.counts.raw + all.false.doubles <- all.false.doubles[which(all.false.doubles[,"varying_bases"] >= 3 & all.false.doubles[,"varying_bases"] < 5 & all.false.doubles[,"varying_codons"] == 2),,drop = F] + all.false.doubles\$codon_dist <- vapply(regmatches(all.false.doubles[,"codon_mut"], gregexpr("\\\\d+(?=:)", all.false.doubles[,"codon_mut"], perl = TRUE)), function(z) abs(diff(as.integer(z))), integer(1)) + + all.false.doubles.codons <- str_split_fixed(all.false.doubles\$codon_mut, ", ", 2) + all.false.doubles.codons[,1] <- as.integer(sub(":.*", "", all.false.doubles.codons[,1])) + all.false.doubles.codons[,2] <- as.integer(sub(":.*", "", all.false.doubles.codons[,2])) + median.high.conf.coverage.per.pos <- rep(NA, max(as.numeric(str_split_fixed(input.counts.processed\$codon_mut, ":", 2)[,1]))) + names(median.high.conf.coverage.per.pos) <- 1:max(as.numeric(str_split_fixed(input.counts.processed\$codon_mut, ":", 2)[,1])) + for(i in 1:length(median.high.conf.coverage.per.pos)){ + median.high.conf.coverage.per.pos[i] <- median(input.counts.processed[which(names(median.high.conf.coverage.per.pos)[i] == str_split_fixed(input.counts.processed\$codon_mut, ":", 2)[,1]),"cov"]) + } + all.false.doubles.codons[,1] <- median.high.conf.coverage.per.pos[match(all.false.doubles.codons[,1], names(median.high.conf.coverage.per.pos))] + all.false.doubles.codons[,2] <- median.high.conf.coverage.per.pos[match(all.false.doubles.codons[,2], names(median.high.conf.coverage.per.pos))] + all.false.doubles\$max_cov_possible <- as.numeric(apply(all.false.doubles.codons, 1, min)) + all.false.doubles\$perc_cov_to_max <- 100 * all.false.doubles\$cov / all.false.doubles\$max_cov_possible + + ## deepmutscan guard (not in the verbatim script) - see the identical guard in the MLE estimator. + ## With no estimable coverage decay there is no evidence of codon-level sequencing error, so the + ## correct answer is a no-op: corrected == raw, rather than lm() dying on an empty frame. + .usable <- is.finite(log(all.false.doubles\$perc_cov_to_max)) + if (nrow(all.false.doubles) == 0 || length(unique(all.false.doubles\$codon_dist[.usable])) < 3) { + cat("WARNING: only", sum(.usable), "usable false double mutant(s) found - the distance-dependent\\n", + "coverage decay is not estimable, so NO sequencing-error correction is applied and the\\n", + "corrected counts are identical to the raw counts.\\n") + write.csv(seq.error.rate, file = seq_error_rate_path, row.names = T) + write.csv(input.counts.processed, file = output_file_path, row.names = F) + return(invisible(NULL)) + } + + fit <- lm(log(all.false.doubles\$perc_cov_to_max) ~ all.false.doubles\$codon_dist + I(all.false.doubles\$codon_dist^2)) + all.false.doubles\$pred_cov_perc <- exp(predict(fit)) + + ### convert this into a look-up table: "% max. possible coverage" depending on codon-codon distance + dual.codon.coverage.estimate <- matrix(NA, nrow = max(all.false.doubles\$codon_dist), ncol = 2) + colnames(dual.codon.coverage.estimate) <- c("Codon distance", "Estimate % max. possible coverage") + dual.codon.coverage.estimate[,"Codon distance"] <- 1:nrow(dual.codon.coverage.estimate) + ## deepmutscan fix (Maxi-approved deviation from the verbatim script): fill the coverage-decay + ## lookup from the fitted quadratic evaluated at EVERY integer codon distance, instead of match()ing + ## only the distances that happen to have an observed double. `pred_cov_perc = exp(predict(fit))` + ## depends solely on codon_dist, so this is bit-identical to the original at every observed distance + ## and merely fills the previously-NA gaps -- those NAs otherwise crash MLE and leave NA corrected + ## counts in EB (a distance with no observed double, e.g. around codon 288 on the GID1A min_counts=10 + ## data). + .dccd <- dual.codon.coverage.estimate[,"Codon distance"] + .cf <- coef(fit) + dual.codon.coverage.estimate[,"Estimate % max. possible coverage"] <- exp(.cf[1] + .cf[2] * .dccd + .cf[3] * .dccd^2) + + ## Process the GATK file, only look at single nucleotide variants + cat("Sequencing error correction of GATK counts...\\n") + out.summary <- matrix(NA, ncol = 7, nrow = length(grep("[,]", input.counts.processed[,"base_mut"], invert = T))) + colnames(out.summary) <- c("SNV", "Category", + "False_double_variant_count", "False_double_variant_coverage", + "True_high_conf_variant_count", "True_high_conf_variant_coverage", + "Exposure_m") + out.summary[,"SNV"] <- input.counts.processed[grep("[,]", input.counts.processed[,"base_mut"], invert = T),"base_mut"] + out.summary[,"Category"] <- str_split_fixed(out.summary[,"SNV"], ":", 2)[,2] + category.translate <- cbind(c("C>A", "C>G", "C>T", "T>A", "T>C", "T>G"), + c("G>T", "G>C", "G>A", "A>T", "A>G", "A>C")) + for(i in 1:nrow(out.summary)){ + if(out.summary[i,"Category"] %in% category.translate[,2]){ + out.summary[i,"Category"] <- category.translate[match(out.summary[i,"Category"],category.translate[,2]),1] + } + } + + for (i in grep("[,]", input.counts.processed[,"base_mut"], invert = T)){ + + ### algorithm: + ### 1.) isolate 1nt variant of interest + ### 2.) look for 2/3nt hits in a pre-specified window of codons upstream or downstream + ### -> if there are zero false double mutants: skip / set correction factor to zero + ### -> if there are any false double mutants: continue + ### 3.) generate a table for ALL possible 2/3nt variants in the selected window: + ### true high-conf. variant count | true high-conf. variant coverage | false double double variant count (often 0 or 1) | false double double variant coverage + ### 4.) Iterate of all positions + ### 5.) Calculate the empirical Bayes + ### 6.) Apply correction factor + + ## 1.) isolate 1nt variant of interest + tmp.single <- input.counts.processed[i,"base_mut"] + seq.error.rate[tmp.single, "1nt counts/coverage"] <- input.counts.processed[i,"counts_per_cov"] + + ## 2.) look for 2/3nt hits in up to 40 codons (120 bp) upstream or downstream + tmp.false.doubles <- input.counts.raw[grep(paste0("(?= 3 & tmp.false.doubles[,"varying_bases"] < 5 & tmp.false.doubles[,"varying_codons"] == 2),,drop = F] + if(nrow(tmp.false.doubles) == 0){ + next + } + + ## set and enforce distance threshold + tmp.ref.codon <- as.numeric(strsplit(input.counts.processed[i,"codon_mut"] , ":")[[1]][1]) + tmp.min.from.ref <- max(c(1, tmp.ref.codon - c(codon_window - 1))) + tmp.max.from.ref <- min(c(max.codon, tmp.ref.codon + c(codon_window - 1))) + tmp.double.codon <- str_split_fixed(tmp.false.doubles\$codon_mut, ", ", 2) + tmp.double.codon.n1 <- as.integer(sub(":.*", "", tmp.double.codon[,1])) + tmp.double.codon.n2 <- as.integer(sub(":.*", "", tmp.double.codon[,2])) + tmp.double.codon.within.range <- (tmp.double.codon.n1 >= tmp.min.from.ref & tmp.double.codon.n1 <= tmp.max.from.ref) & (tmp.double.codon.n2 >= tmp.min.from.ref & tmp.double.codon.n2 <= tmp.max.from.ref) + tmp.false.doubles <- tmp.false.doubles[which(tmp.double.codon.within.range == T),,drop = F] + if(nrow(tmp.false.doubles) == 0){ + next + } + + ## 3.) generate a table for ALL possible 2/3nt variants in the selected window + tmp.summary.table.entries <- input.counts.processed + tmp.summary.table.entries <- tmp.summary.table.entries[which(as.numeric(str_split_fixed(tmp.summary.table.entries\$codon_mut, ":", 2)[,1]) != tmp.ref.codon),] + tmp.summary.table.entries <- tmp.summary.table.entries[which(as.numeric(str_split_fixed(tmp.summary.table.entries\$codon_mut, ":", 2)[,1]) >= tmp.min.from.ref & + as.numeric(str_split_fixed(tmp.summary.table.entries\$codon_mut, ":", 2)[,1]) <= tmp.max.from.ref),] + tmp.summary.table.entries <- tmp.summary.table.entries[which(tmp.summary.table.entries\$varying_bases > 1),] + + ### fill the table + tmp.summary.table <- matrix(NA, ncol = 5, nrow = nrow(tmp.summary.table.entries)) + colnames(tmp.summary.table) <- c("codon distance", + "true high-conf. variant count", + "true high-conf. variant coverage", + "false double double variant count", + "false double double variant coverage") + rownames(tmp.summary.table) <- paste0(c(tmp.summary.table.entries\$codon_mut),", ", c(tmp.summary.table.entries\$base_mut)) + + tmp.summary.table[,"codon distance"] <- abs(as.numeric(str_split_fixed(tmp.summary.table.entries\$codon_mut, ":", 2)[,1]) - tmp.ref.codon) + tmp.summary.table[,"true high-conf. variant count"] <- tmp.summary.table.entries\$counts + tmp.summary.table[,"true high-conf. variant coverage"] <- tmp.summary.table.entries\$cov + tmp.summary.table[,"false double double variant count"] <- 0 + + ## input the actual hits + tmp.double.codon <- str_split_fixed(tmp.false.doubles\$codon_mut, ", ", 2) + tmp.match1 <- match(tmp.double.codon[,1], str_split_fixed(rownames(tmp.summary.table), ", ", 2)[,1]) + tmp.match1[is.na(tmp.match1)] <- 0 + tmp.match2 <- match(tmp.double.codon[,2], str_split_fixed(rownames(tmp.summary.table), ", ", 2)[,1]) + tmp.match2[is.na(tmp.match2)] <- 0 + tmp.match <- c(tmp.match1 + tmp.match2) + + ### remove those false doubles for which there are no matched true high confidence calls + if(any(tmp.match == 0)){ + tmp.false.doubles <- tmp.false.doubles[-which(tmp.match == 0),,drop = F] + tmp.match <- tmp.match[-which(tmp.match == 0)] + if(nrow(tmp.false.doubles) == 0){ + next + } + } + + tmp.summary.table[tmp.match,"false double double variant count"] <- tmp.false.doubles[,"counts"] + tmp.summary.table[tmp.match,"false double double variant coverage"] <- tmp.false.doubles[,"cov"] + tmp.summary.table <- as.data.frame(tmp.summary.table) + + ## 4.) Iterate over codon positions + + ### summarise data by position + tmp.summary.table.pos <- matrix(NA, nrow = length(unique(str_split_fixed(rownames(tmp.summary.table), ">", 2)[,1])), ncol = 5) + colnames(tmp.summary.table.pos) <- colnames(tmp.summary.table) + rownames(tmp.summary.table.pos) <- unique(str_split_fixed(rownames(tmp.summary.table), ">", 2)[,1]) + tmp.summary.table.pos[,"codon distance"] <- abs(as.numeric(str_split_fixed(rownames(tmp.summary.table.pos), ":", 2)[,1]) - as.numeric(str_split_fixed(input.counts.processed[i,"codon_mut"], ":", 2)[,1])) + for(j in 1:nrow(tmp.summary.table.pos)){ + tmp.name <- rownames(tmp.summary.table.pos)[j] + tmp.summary.table.pos[j,"true high-conf. variant count"] <- sum(tmp.summary.table[grep(paste0("^",tmp.name), rownames(tmp.summary.table)),"true high-conf. variant count"]) + tmp.summary.table.pos[j,"true high-conf. variant coverage"] <- max(tmp.summary.table[grep(paste0("^",tmp.name), rownames(tmp.summary.table)),"true high-conf. variant coverage"]) + tmp.summary.table.pos[j,"false double double variant count"] <- sum(tmp.summary.table[grep(paste0("^",tmp.name), rownames(tmp.summary.table)),"false double double variant count"]) + tmp.summary.table.pos[j,"false double double variant coverage"] <- max(tmp.summary.table[grep(paste0("^",tmp.name), rownames(tmp.summary.table)),"false double double variant coverage"], na.rm = T) + + ## infer a good proxy for the false double variant coverage with 0 counts (based estimated distance-dependent % coverage decay observed across all observed double mutants) + if(tmp.summary.table.pos[j,"false double double variant coverage"] == "-Inf"){ + tmp.summary.table.pos[j,"false double double variant coverage"] <- c(dual.codon.coverage.estimate[abs(tmp.summary.table.pos[j,"codon distance"]),"Estimate % max. possible coverage"] / 100) * tmp.summary.table.pos[j,"true high-conf. variant coverage"] + tmp.summary.table.pos[j,"false double double variant coverage"] <- round(tmp.summary.table.pos[j,"false double double variant coverage"]) + } + } + tmp.summary.table.pos <- as.data.frame(tmp.summary.table.pos) + + ### fill the master table + out.summary[match(tmp.single,out.summary[,"SNV"]),"False_double_variant_count"] <- sum(tmp.summary.table.pos\$`false double double variant count`) + out.summary[match(tmp.single,out.summary[,"SNV"]),"False_double_variant_coverage"] <- sum(tmp.summary.table.pos\$`false double double variant coverage`) + out.summary[match(tmp.single,out.summary[,"SNV"]),"True_high_conf_variant_count"] <- sum(tmp.summary.table.pos\$`true high-conf. variant count`) + out.summary[match(tmp.single,out.summary[,"SNV"]),"True_high_conf_variant_coverage"] <- sum(tmp.summary.table.pos\$`true high-conf. variant coverage`) + out.summary[match(tmp.single,out.summary[,"SNV"]),"Exposure_m"] <- sum(tmp.summary.table.pos\$`true high-conf. variant count` * c(tmp.summary.table.pos\$`false double double variant coverage` / tmp.summary.table.pos\$`true high-conf. variant coverage`)) + + } + + ## 5.) Calculate the empirical-Bayes sequencing error estimates + out.summary <- out.summary[-which(is.na(out.summary[,"False_double_variant_count"]) == T),] + out.summary <- as.data.frame(out.summary) + class(out.summary\$False_double_variant_count) <- "numeric" + class(out.summary\$False_double_variant_coverage) <- "numeric" + class(out.summary\$True_high_conf_variant_count) <- "numeric" + class(out.summary\$True_high_conf_variant_coverage) <- "numeric" + class(out.summary\$Exposure_m) <- "numeric" + + ### calculate e_MLE across all + out.summary\$e_MLE <- out.summary\$False_double_variant_count / out.summary\$Exposure_m + + ### calculate all the necessary rates, priors, etc. + + #### global background rate + mu_global <- sum(out.summary\$False_double_variant_count) / sum(out.summary\$Exposure_m) + mu_global <- max(mu_global, 1e-12) ## + + ##### marginal log-likelihood under Gamma-Poisson with fixed mean mu (ChatGPT function, adapted) + ## prior: e ~ Gamma(shape = nu * mu, rate = nu) + ## mean = mu, variance = mu / nu + gp_loglik_nu <- function(log_nu, y, m, mu) { + nu <- exp(log_nu) + a <- max(nu * mu, 1e-12) + + # Negative-binomial marginal: + # y ~ NB(size = a, prob = nu / (nu + m)) + ll <- lgamma(y + a) - lgamma(a) - lgamma(y + 1) + + a * log(nu / (nu + m)) + + y * log(m / (nu + m)) + + -sum(ll) + } + opt_global <- optimize(f = gp_loglik_nu, + interval = c(log(1e-6), log(1e6)), + y = out.summary\$False_double_variant_count, + m = out.summary\$Exposure_m, + mu = mu_global) + nu_global <- exp(opt_global\$minimum) + + #### category-level rates, shrunk to global rate + mu_cat <- vector(mode = "list", length = 6) + names(mu_cat) <- category.translate[,1] + mu_cat_raw <- mu_cat + for(i in 1:length(mu_cat)){ + mu_cat[[i]] <- out.summary[which(out.summary\$Category == names(mu_cat)[i]),] + mu_cat_raw[[i]] <- sum(mu_cat[[i]]\$False_double_variant_count) / sum(mu_cat[[i]]\$Exposure_m) + mu_cat_raw[[i]] <- pmax(mu_cat_raw[[i]], 1e-12) + mu_cat[[i]] <- c(sum(mu_cat[[i]]\$False_double_variant_count) + nu_global * mu_global) / c(sum(mu_cat[[i]]\$Exposure_m) + nu_global) + mu_cat[[i]] <- pmax(mu_cat[[i]], 1e-12) + } + mu_cat_raw <- do.call(c, mu_cat_raw) + mu_cat <- do.call(c, mu_cat) + + ##### category-specific prior strength nu_cat (ChatGPT function, adapted) + ## with fallback to global if category is too small + nu_cat <- rep(NA, 6) + names(nu_cat) <- names(mu_cat) + for (i in 1:length(mu_cat)) { + cat_name <- names(mu_cat)[i] + idx <- out.summary\$Category == cat_name + + yk <- out.summary\$False_double_variant_count[idx] + mk <- out.summary\$Exposure_m[idx] + mu_k <- mu_cat[i] + + opt_k <- try(optimize(f = gp_loglik_nu, + interval = c(log(1e-6), log(1e6)), + y = yk, + m = mk, + mu = mu_k), + silent = TRUE) + + if (inherits(opt_k, "try-error")) { + nu_cat[i] <- nu_global + } else { + nu_cat[i] <- exp(opt_k\$minimum) + } + } + + #### posterior mean for each specific SNV + out.summary\$mu_category <- rep(NA, nrow(out.summary)) + out.summary\$nu_category <- rep(NA, nrow(out.summary)) + for(i in 1:6){ + out.summary\$mu_category[which(out.summary\$Category == names(mu_cat)[i])] <- mu_cat[i] + out.summary\$nu_category[which(out.summary\$Category == names(nu_cat)[i])] <- nu_cat[i] + } + + ##### prior: e_j ~ Gamma(shape = nu_cat * mu_cat, rate = nu_cat) + out.summary\$prior_shape <- out.summary\$mu_category * out.summary\$nu_category + out.summary\$prior_rate <- out.summary\$nu_category + + ##### posterior: e_j | data ~ Gamma(shape = prior_shape + F, rate = prior_rate [= nu_cat] + M) + out.summary\$post_shape <- out.summary\$prior_shape + out.summary\$False_double_variant_count + out.summary\$post_rate <- out.summary\$nu_category + out.summary\$Exposure_m + + ##### posterior mean = shrunk EB estimate + out.summary\$e_EB <- out.summary\$post_shape / out.summary\$post_rate + + ##### other posterior intervals + # out.summary\$e_EB_upper_1SD <- qgamma(0.68, shape = out.summary\$post_shape, rate = out.summary\$post_rate) + # out.summary\$e_EB_upper_2SD <- qgamma(0.95, shape = out.summary\$post_shape, rate = out.summary\$post_rate) + # out.summary\$e_EB_upper_3SD <- qgamma(0.997, shape = out.summary\$post_shape, rate = out.summary\$post_rate) + + ## 6.) Apply correction factors systematically (use upper 95% percentile to correct, a.k.a. "conservative") + seq.error.rate[match(out.summary\$SNV, rownames(seq.error.rate)),"1nt false counts/coverage"] <- out.summary\$e_EB + input.counts.processed[match(out.summary\$SNV, input.counts.processed\$base_mut),"counts_per_cov_corrected"] <- input.counts.processed[match(out.summary\$SNV, input.counts.processed\$base_mut),"counts_per_cov"] - out.summary\$e_EB + input.counts.processed[match(out.summary\$SNV, input.counts.processed\$base_mut),"counts_corrected"] <- input.counts.processed[match(out.summary\$SNV, input.counts.processed\$base_mut),"counts"] - c(out.summary\$e_EB * input.counts.processed[match(out.summary\$SNV, input.counts.processed\$base_mut),"cov"]) + + ## round to nearest integer, do not allow for negative counts + input.counts.processed[match(out.summary\$SNV, input.counts.processed\$base_mut),"counts_corrected"] <- round(input.counts.processed[match(out.summary\$SNV, input.counts.processed\$base_mut),"counts_corrected"]) + if(any(which(input.counts.processed[,"counts_corrected"] < 0))){ + input.counts.processed[which(input.counts.processed[,"counts_corrected"] < 0),"counts_per_cov_corrected"] <- 0 + input.counts.processed[which(input.counts.processed[,"counts_corrected"] < 0),"counts_corrected"] <- 0 + } + + # Write the processed data + write.csv(seq.error.rate, file = seq_error_rate_path, row.names = T) + write.csv(input.counts.processed, file = output_file_path, row.names = F) + +} + +## ------------------------------------------------------------------------------------------------- +## I/O wrappers preserved from the pipeline module (not part of Maxi's estimator logic) +## ------------------------------------------------------------------------------------------------- + +# propagate corrected counts onto the completed library table (used by e.g. logdiff) +correct_library_completed <- function(completed_path, corrected_filtered_path, output_file_path){ + completed <- read.csv(completed_path) + corrected <- read.csv(corrected_filtered_path) + idx <- match(completed\$codon_mut, corrected\$codon_mut) + has <- !is.na(idx) + completed\$counts[has] <- corrected\$counts[idx[has]] + completed\$counts_per_cov[has] <- corrected\$counts_per_cov[idx[has]] + write.csv(completed, file = output_file_path, row.names = FALSE) +} + +# re-make the AA-level heatmap table from corrected counts (canonical column names) +seq_error_correct_counts_for_heatmaps <- function(input_csv_path, aa_seq_file_path, output_csv_path, threshold = 3) { + + raw_counts <- read.table(input_csv_path, sep = ",", header = TRUE, stringsAsFactors = FALSE) + + wt_seq <- readLines(aa_seq_file_path) + wt_seq <- unlist(strsplit(wt_seq, "")) + wt_seq[wt_seq == "X"] <- "*" + + # summarise corrected counts for each unique pos_mut + aggregated_counts_per_cov <- aggregate( + counts_per_cov_corrected ~ pos_mut, + data = raw_counts, + FUN = function(x) sum(x, na.rm = TRUE) + ) + aggregated_counts <- aggregate( + counts_corrected ~ pos_mut, + data = raw_counts, + FUN = function(x) sum(x, na.rm = TRUE) + ) + aggregated_data <- merge(aggregated_counts_per_cov, aggregated_counts, by = "pos_mut", all = TRUE) + + # canonical output names so the count heatmaps consume this drop-in + names(aggregated_data)[names(aggregated_data) == "counts_per_cov_corrected"] <- "total_counts_per_cov" + names(aggregated_data)[names(aggregated_data) == "counts_corrected"] <- "total_counts" + + aggregated_data\$wt_aa <- sub("(\\\\D)(\\\\d+)(\\\\D)", "\\\\1", aggregated_data\$pos_mut) + aggregated_data\$position <- as.numeric(sub("(\\\\D)(\\\\d+)(\\\\D)", "\\\\2", aggregated_data\$pos_mut)) + aggregated_data\$mut_aa <- sub("(\\\\D)(\\\\d+)(\\\\D)", "\\\\3", aggregated_data\$pos_mut) + aggregated_data\$mut_aa[aggregated_data\$mut_aa == "X"] <- "*" + + all_amino_acids <- c("A", "C", "D", "E", "F", "G", "H", "I", "K", "L", + "M", "N", "P", "Q", "R", "S", "T", "V", "W", "Y", "*") + all_positions <- seq_along(wt_seq) + complete_data <- expand.grid(mut_aa = all_amino_acids, position = all_positions, stringsAsFactors = FALSE) + + heatmap_data <- merge(complete_data, aggregated_data, by = c("mut_aa", "position"), all.x = TRUE, sort = FALSE) + + heatmap_data\$total_counts_per_cov[is.na(heatmap_data\$total_counts_per_cov)] <- 0 + heatmap_data\$wt_aa <- wt_seq[heatmap_data\$position] + + low_count <- is.na(heatmap_data\$total_counts) | heatmap_data\$total_counts < threshold + heatmap_data\$total_counts_per_cov[low_count] <- NA + heatmap_data\$total_counts[low_count] <- NA + + missing_pos_mut <- is.na(heatmap_data\$pos_mut) + heatmap_data\$pos_mut[missing_pos_mut] <- paste0( + heatmap_data\$wt_aa[missing_pos_mut], + heatmap_data\$position[missing_pos_mut], + heatmap_data\$mut_aa[missing_pos_mut] + ) + + out.order <- paste0(rep(1:max(heatmap_data\$position), each = 21), + rep(c("A", "C", "D", "E", "F", "G", "H", + "I", "K", "L","M", "N", "P", "Q", + "R", "S", "T", "V", "W", "Y", "*"), + max(heatmap_data\$position))) + heatmap_data <- heatmap_data[match(out.order, paste0(heatmap_data\$position, heatmap_data\$mut_aa)),] + rownames(heatmap_data) <- 1:nrow(heatmap_data) + + write.csv(heatmap_data, file = output_csv_path, row.names = FALSE) + +} + +# Defensive coordinate-contract check (general, not GID1A-specific): base_mut positions must be +# 1-based within the supplied reference, and the stated WT base must match the reference base there. +# A mismatch means the reference and the counts are in different frames -> stop rather than +# silently mis-index seq.error.rate. +check_reference_frame <- function(wt_path, processed_path){ + ref <- strsplit(as.character(readDNAStringSet(wt_path)[[1]]), "")[[1]] + proc <- read.csv(processed_path) + snv <- proc[grep("[,]", proc[,"base_mut"], invert = TRUE), "base_mut"] + if(length(snv) == 0) return(invisible(NULL)) + pos <- suppressWarnings(as.integer(str_split_fixed(snv, ":", 2)[,1])) + wtbase <- substr(str_split_fixed(snv, ":", 2)[,2], 1, 1) + ok <- !is.na(pos) & pos >= 1 & pos <= length(ref) + if(any(!ok)){ + stop("Reference/counts coordinate mismatch: ", sum(!ok), " single-nucleotide base_mut positions ", + "fall outside the reference (length ", length(ref), "). The reference fasta must be the same ", + "one the reads were aligned to, in the same coordinate frame as base_mut.") + } + mism <- ref[pos] != wtbase + if(any(mism, na.rm = TRUE)){ + stop("Reference/counts coordinate mismatch: ", sum(mism, na.rm = TRUE), " single-nucleotide ", + "base_mut entries state a wild-type base that disagrees with the reference at that position ", + "(e.g. ", snv[which(mism)[1]], "). Check that --fasta matches the counts.") + } + invisible(NULL) +} + +## ------------------------------------------------------------------------------------------------- +## Run +## ------------------------------------------------------------------------------------------------- +method <- "$method" +codon_window <- $codon_window + +# fail loudly if the supplied reference and the counts are in different coordinate frames +check_reference_frame("$wt_fasta", "$filtered") + +intermediate <- "variantCounts_intermediate_corrected.csv" +if (method == "eb") { + seq_error_correct_by_false_doubles_EB( + wt_path = "$wt_fasta", + input_count_path_raw = "$raw_counts", + input_count_path_processed = "$filtered", + output_file_path = intermediate, + seq_error_rate_path = "seq_error_rate.csv", + codon_window = codon_window + ) +} else { + seq_error_correct_by_false_doubles_MLE( + wt_path = "$wt_fasta", + input_count_path_raw = "$raw_counts", + input_count_path_processed = "$filtered", + output_file_path = intermediate, + seq_error_rate_path = "seq_error_rate.csv", + codon_window = codon_window + ) +} + +## corrected counts become the canonical columns so every count-dependent step uses them; +## the originals are preserved as *_raw for the error-correction report +ic <- read.csv(intermediate) +ic\$counts_raw <- ic\$counts +ic\$counts_per_cov_raw <- ic\$counts_per_cov +ic\$counts <- ic\$counts_corrected +ic\$counts_per_cov <- ic\$counts_per_cov_corrected +write.csv(ic, file = "variantCounts_filtered_by_library_error_corrected.csv", row.names = FALSE) + +seq_error_correct_counts_for_heatmaps( + input_csv_path = "variantCounts_filtered_by_library_error_corrected.csv", + aa_seq_file_path = "$aa_seq", + output_csv_path = "variantCounts_for_heatmaps_error_corrected.csv", + threshold = $min_counts +) + +correct_library_completed( + completed_path = "$completed", + corrected_filtered_path = "variantCounts_filtered_by_library_error_corrected.csv", + output_file_path = "library_completed_variantCounts_error_corrected.csv" +) + +## --- versions --- +r_version <- strsplit(version[['version.string']], ' ')[[1]][3] +if (is.null(r_version)) r_version <- "unknown" +stringr_version <- as.character(packageVersion("stringr")) +biostrings_version <- as.character(packageVersion("Biostrings")) +f <- file("versions.yml", "w") +writeLines( + c( + '"${task.process}":', + paste(' r-base:', r_version), + paste(' r-stringr:', stringr_version), + paste(' r-Biostrings:', biostrings_version) + ), + f +) +close(f) diff --git a/modules/local/dmsanalysis/error_correction_wildtype/environment.yml b/modules/local/dmsanalysis/error_correction_wildtype/environment.yml new file mode 100644 index 0000000..9db6b17 --- /dev/null +++ b/modules/local/dmsanalysis/error_correction_wildtype/environment.yml @@ -0,0 +1,4 @@ +channels: + - conda-forge +dependencies: + - conda-forge::r-base=4.5.1 diff --git a/modules/local/dmsanalysis/error_correction_wildtype/main.nf b/modules/local/dmsanalysis/error_correction_wildtype/main.nf new file mode 100644 index 0000000..0937129 --- /dev/null +++ b/modules/local/dmsanalysis/error_correction_wildtype/main.nf @@ -0,0 +1,38 @@ +process DMSANALYSIS_ERROR_CORRECTION_WILDTYPE { + tag "$meta.id" + label 'process_single' + + conda "${moduleDir}/environment.yml" + + container "${ workflow.containerEngine == 'singularity' + ? 'https://community-cr-prod.seqera.io/docker/registry/v2/blobs/sha256/73/73a72ec77725aeb67678a74228938fdd6827b669d01a8c96951b1a8ef96eeb0f/data' + : 'community.wave.seqera.io/library/bioconductor-biostrings_bioconductor-mutscan_r-base_r-biocmanager_pruned:c65036d76406f342' }" + + input: + tuple val(meta), path(filtered, stageAs: 'input_counts.csv'), path(wt_counts, stageAs: 'wildtype_counts.csv'), path(completed) + path aa_seq + val min_counts + + output: + tuple val(meta), + path("variantCounts_filtered_by_library_error_corrected.csv"), + path("variantCounts_for_heatmaps_error_corrected.csv"), + path("library_completed_variantCounts_error_corrected.csv"), + emit: corrected + path "versions.yml", emit: versions + + when: + task.ext.when == null || task.ext.when + + script: + template 'error_correction_wildtype.R' + + stub: + """ + touch variantCounts_filtered_by_library_error_corrected.csv + touch variantCounts_for_heatmaps_error_corrected.csv + touch library_completed_variantCounts_error_corrected.csv + echo "DMSANALYSIS_ERROR_CORRECTION_WILDTYPE:" > versions.yml + echo " stub-version: 0.0.0" >> versions.yml + """ +} diff --git a/modules/local/dmsanalysis/error_correction_wildtype/meta.yml b/modules/local/dmsanalysis/error_correction_wildtype/meta.yml new file mode 100644 index 0000000..8a38af2 --- /dev/null +++ b/modules/local/dmsanalysis/error_correction_wildtype/meta.yml @@ -0,0 +1,66 @@ +name: "dmsanalysis_error_correction_wildtype" +description: "Sequencing-error correction of single-codon variant counts using an additional deep wildtype-only sequencing sample to measure the position-specific error profile, which is then subtracted from each library sample." +keywords: + - deep mutational scanning + - error correction + - wildtype sequencing + - variant counting +tools: + - "r-base": + description: "The R programming language (base only)" + homepage: "https://www.r-project.org/" + licence: ["GPL-2.0-or-later"] + +input: + - - meta: + type: map + description: | + Groovy Map containing sample information + e.g. `[ id:'test', sample:'ORF1', type:'input', replicate:1 ]` + - filtered: + type: file + description: Library-filtered single-codon variant counts (annotated CSV) for this sample. + pattern: "*.csv" + - wt_counts: + type: file + description: Library-filtered variant counts of the matching wildtype-only sample. + pattern: "*.csv" + - completed: + type: file + description: Completed library table (all possible variants) to also correct. + pattern: "*.csv" + - - aa_seq: + type: file + description: Reference amino-acid sequence (one line). + - - min_counts: + type: integer + description: Minimum count threshold for the amino-acid-level heatmap table. + +output: + - corrected: + - meta: + type: map + description: Groovy Map containing sample information + - variantCounts_filtered_by_library_error_corrected.csv: + type: file + description: Library-filtered counts with added `counts_corrected` / `counts_per_cov_corrected` columns. + pattern: "variantCounts_filtered_by_library_error_corrected.csv" + - variantCounts_for_heatmaps_error_corrected.csv: + type: file + description: Amino-acid-level heatmap table built from corrected counts (canonical `total_counts` columns). + pattern: "variantCounts_for_heatmaps_error_corrected.csv" + - library_completed_variantCounts_error_corrected.csv: + type: file + description: Completed library table with corrected counts propagated (for logdiff etc.). + pattern: "library_completed_variantCounts_error_corrected.csv" + - versions: + - versions.yml: + type: file + description: File containing software versions + pattern: "versions.yml" + +authors: + - "@BenjaminWehnert1008" + - "@MaximilianStammnitz" +maintainers: + - "@BenjaminWehnert1008" diff --git a/modules/local/dmsanalysis/error_correction_wildtype/templates/error_correction_wildtype.R b/modules/local/dmsanalysis/error_correction_wildtype/templates/error_correction_wildtype.R new file mode 100644 index 0000000..1e82965 --- /dev/null +++ b/modules/local/dmsanalysis/error_correction_wildtype/templates/error_correction_wildtype.R @@ -0,0 +1,159 @@ +#!/usr/bin/env Rscript + +## Wildtype-based sequencing error correction in nf-core/deepmutscan +## An additional deep wildtype-only sequencing sample measures the position-specific error +## profile, which is subtracted from each library sample's per-base coverage. + +## --- Helper functions --- + +# WT-based sequencing error correction (nucleotide level) +seq_error_correct_by_WT_nt <- function(wt_seq_count_path, input_count_path, output_file_path){ + + WT.counts <- read.csv(wt_seq_count_path) + input.counts <- read.csv(input_count_path) + + ## append key columns + input.counts\$counts_corrected <- input.counts\$counts + input.counts\$counts_per_cov_corrected <- input.counts\$counts_per_cov + + for (i in 1:nrow(WT.counts)){ + + ## only look at variants observed in both the input and WT sequencing + tmp.id <- match(WT.counts[i,"base_mut"], input.counts[,"base_mut"]) + if(is.na(tmp.id) == T){ + next + } + + ## subtract the observed per-base coverage in the WT sequencing + input.counts[tmp.id,"counts_per_cov_corrected"] <- input.counts[tmp.id,"counts_per_cov"] - WT.counts[i,"counts_per_cov"] + + ## adjust the total counts by cross-multiplication + input.counts[tmp.id,"counts_corrected"] <- input.counts[tmp.id,"counts"] * c(input.counts[tmp.id,"counts_per_cov_corrected"] / input.counts[tmp.id,"counts_per_cov"]) + + ## round to nearest integer, do not allow for negative counts + input.counts[tmp.id,"counts_corrected"] <- round(input.counts[tmp.id,"counts_corrected"]) + if(input.counts[tmp.id,"counts_corrected"] < 0){ + input.counts[tmp.id,"counts_corrected"] <- 0 + input.counts[tmp.id,"counts_per_cov_corrected"] <- 0 + } + + } + + ## corrected counts become the canonical columns so every count-dependent step uses them; + ## the originals are preserved as *_raw for the error-correction report + input.counts\$counts_raw <- input.counts\$counts + input.counts\$counts_per_cov_raw <- input.counts\$counts_per_cov + input.counts\$counts <- input.counts\$counts_corrected + input.counts\$counts_per_cov <- input.counts\$counts_per_cov_corrected + + write.csv(input.counts, file = output_file_path, row.names = F) + +} + +# propagate corrected counts onto the completed library table (used by e.g. logdiff) +correct_library_completed <- function(completed_path, corrected_filtered_path, output_file_path){ + completed <- read.csv(completed_path) + corrected <- read.csv(corrected_filtered_path) + idx <- match(completed\$codon_mut, corrected\$codon_mut) + has <- !is.na(idx) + completed\$counts[has] <- corrected\$counts[idx[has]] + completed\$counts_per_cov[has] <- corrected\$counts_per_cov[idx[has]] + write.csv(completed, file = output_file_path, row.names = FALSE) +} + +# re-make the AA-level heatmap table from corrected counts (canonical column names) +seq_error_correct_counts_for_heatmaps <- function(input_csv_path, aa_seq_file_path, output_csv_path, threshold = 3) { + + raw_counts <- read.table(input_csv_path, sep = ",", header = TRUE, stringsAsFactors = FALSE) + + wt_seq <- readLines(aa_seq_file_path) + wt_seq <- unlist(strsplit(wt_seq, "")) + wt_seq[wt_seq == "X"] <- "*" + + # summarise corrected counts for each unique pos_mut + aggregated_counts_per_cov <- aggregate( + counts_per_cov_corrected ~ pos_mut, + data = raw_counts, + FUN = function(x) sum(x, na.rm = TRUE) + ) + aggregated_counts <- aggregate( + counts_corrected ~ pos_mut, + data = raw_counts, + FUN = function(x) sum(x, na.rm = TRUE) + ) + aggregated_data <- merge(aggregated_counts_per_cov, aggregated_counts, by = "pos_mut", all = TRUE) + + # canonical output names so the count heatmaps consume this drop-in + names(aggregated_data)[names(aggregated_data) == "counts_per_cov_corrected"] <- "total_counts_per_cov" + names(aggregated_data)[names(aggregated_data) == "counts_corrected"] <- "total_counts" + + aggregated_data\$wt_aa <- sub("(\\\\D)(\\\\d+)(\\\\D)", "\\\\1", aggregated_data\$pos_mut) + aggregated_data\$position <- as.numeric(sub("(\\\\D)(\\\\d+)(\\\\D)", "\\\\2", aggregated_data\$pos_mut)) + aggregated_data\$mut_aa <- sub("(\\\\D)(\\\\d+)(\\\\D)", "\\\\3", aggregated_data\$pos_mut) + aggregated_data\$mut_aa[aggregated_data\$mut_aa == "X"] <- "*" + + all_amino_acids <- c("A", "C", "D", "E", "F", "G", "H", "I", "K", "L", + "M", "N", "P", "Q", "R", "S", "T", "V", "W", "Y", "*") + all_positions <- seq_along(wt_seq) + complete_data <- expand.grid(mut_aa = all_amino_acids, position = all_positions, stringsAsFactors = FALSE) + + heatmap_data <- merge(complete_data, aggregated_data, by = c("mut_aa", "position"), all.x = TRUE, sort = FALSE) + + heatmap_data\$total_counts_per_cov[is.na(heatmap_data\$total_counts_per_cov)] <- 0 + heatmap_data\$wt_aa <- wt_seq[heatmap_data\$position] + + low_count <- is.na(heatmap_data\$total_counts) | heatmap_data\$total_counts < threshold + heatmap_data\$total_counts_per_cov[low_count] <- NA + heatmap_data\$total_counts[low_count] <- NA + + missing_pos_mut <- is.na(heatmap_data\$pos_mut) + heatmap_data\$pos_mut[missing_pos_mut] <- paste0( + heatmap_data\$wt_aa[missing_pos_mut], + heatmap_data\$position[missing_pos_mut], + heatmap_data\$mut_aa[missing_pos_mut] + ) + + out.order <- paste0(rep(1:max(heatmap_data\$position), each = 21), + rep(c("A", "C", "D", "E", "F", "G", "H", + "I", "K", "L","M", "N", "P", "Q", + "R", "S", "T", "V", "W", "Y", "*"), + max(heatmap_data\$position))) + heatmap_data <- heatmap_data[match(out.order, paste0(heatmap_data\$position, heatmap_data\$mut_aa)),] + rownames(heatmap_data) <- 1:nrow(heatmap_data) + + write.csv(heatmap_data, file = output_csv_path, row.names = FALSE) + +} + +## --- Run --- +seq_error_correct_by_WT_nt( + wt_seq_count_path = "$wt_counts", + input_count_path = "$filtered", + output_file_path = "variantCounts_filtered_by_library_error_corrected.csv" +) + +seq_error_correct_counts_for_heatmaps( + input_csv_path = "variantCounts_filtered_by_library_error_corrected.csv", + aa_seq_file_path = "$aa_seq", + output_csv_path = "variantCounts_for_heatmaps_error_corrected.csv", + threshold = $min_counts +) + +correct_library_completed( + completed_path = "$completed", + corrected_filtered_path = "variantCounts_filtered_by_library_error_corrected.csv", + output_file_path = "library_completed_variantCounts_error_corrected.csv" +) + +## --- versions --- +r_version <- strsplit(version[['version.string']], ' ')[[1]][3] +if (is.null(r_version)) r_version <- "unknown" +f <- file("versions.yml", "w") +writeLines( + c( + '"${task.process}":', + paste(' r-base:', r_version) + ), + f +) +close(f) diff --git a/modules/local/dmsanalysis/possible_mutations/environment.yml b/modules/local/dmsanalysis/possible_mutations/environment.yml new file mode 100644 index 0000000..197283a --- /dev/null +++ b/modules/local/dmsanalysis/possible_mutations/environment.yml @@ -0,0 +1,15 @@ +channels: + - conda-forge + - bioconda +dependencies: + - bioconda::bioconductor-biostrings=2.78.0 + - conda-forge::r-base=4.5.1 + - conda-forge::r-biocmanager=1.30.27 + - conda-forge::r-dplyr=1.1.4 + - conda-forge::r-ggplot2=4.0.2 + - conda-forge::r-reshape2=1.4.4 + - conda-forge::r-scales=1.4.0 + - conda-forge::r-stringr=1.6.0 + - conda-forge::r-tidyr=1.3.1 + - conda-forge::r-tidyverse=2.0.0 + - conda-forge::r-zoo=1.8_15 diff --git a/modules/local/dmsanalysis/possible_mutations/main.nf b/modules/local/dmsanalysis/possible_mutations/main.nf new file mode 100644 index 0000000..e98eb0f --- /dev/null +++ b/modules/local/dmsanalysis/possible_mutations/main.nf @@ -0,0 +1,33 @@ +process DMSANALYSIS_POSSIBLE_MUTATIONS { + tag "table /w all possible variants" + label 'process_single' + + conda "${moduleDir}/environment.yml" + + container "${ workflow.containerEngine == 'singularity' + ? 'https://community-cr-prod.seqera.io/docker/registry/v2/blobs/sha256/73/73a72ec77725aeb67678a74228938fdd6827b669d01a8c96951b1a8ef96eeb0f/data' + : 'community.wave.seqera.io/library/bioconductor-biostrings_bioconductor-mutscan_r-base_r-biocmanager_pruned:c65036d76406f342' }" + + input: + tuple val(meta), path(wt_seq) + val pos_range + val mutagenesis_type + path custom_codon_library + + output: + tuple val(meta), path("possible_mutations.csv"), emit: possible_mutations + path "versions.yml", emit: versions + + when: + task.ext.when == null || task.ext.when + + script: + template 'possible_mutations.R' + + stub: + """ + touch possible_mutations.csv + echo "DMSANALYSIS_POSSIBLE_MUTATIONS:" > versions.yml + echo " stub-version: 0.0.0" >> versions.yml + """ +} diff --git a/modules/local/dmsanalysis/possible_mutations/meta.yml b/modules/local/dmsanalysis/possible_mutations/meta.yml new file mode 100644 index 0000000..4fbd8c3 --- /dev/null +++ b/modules/local/dmsanalysis/possible_mutations/meta.yml @@ -0,0 +1,63 @@ +name: "dmsanalysis_possible_mutations" +description: | + Generates a comprehensive table of all theoretically possible variants based on the + mutagenesis strategy (e.g., NNK, NNS) and the reference sequence. This table acts + as the ground truth for downstream filtering and analysis. +keywords: + - deep mutational scanning + - dms + - mutagenesis + - library design + - variants +tools: + - "mutscan": + description: "R package for analysis of deep mutational scanning data" + homepage: "https://bioconductor.org/packages/release/bioc/html/mutscan.html" + documentation: "https://bioconductor.org/packages/release/bioc/manuals/mutscan/man/mutscan.pdf" + tool_dev_url: "https://github.com/csoneson/mutscan" + doi: "10.1186/s12859-023-05187-y" + licence: ["GPL-3.0-or-later"] + +input: + - - meta: + type: map + description: | + Groovy Map containing sample information + e.g. `[ id:'ref' ]` + - wt_seq: + type: file + description: FASTA file containing the wild-type reference DNA sequence + pattern: "*.{fasta,fa}" + - - pos_range: + type: string + description: Start and stop codon positions (ORF) in the format 'start-stop', e.g., '352-1383' + - - mutagenesis_type: + type: string + description: | + Type of mutagenic primers used. + Supported types: nnk, nns, nnh, nnn, nnk_nns, nnk_nns_nnh, or custom. + - - custom_codon_library: + type: file + description: | + Optional CSV file defining a custom codon library. + Only required if mutagenesis_type is set to 'custom'. + +output: + - possible_mutations: + - meta: + type: map + description: | + Groovy Map containing sample information + - possible_mutations.csv: + type: file + description: CSV file containing all theoretically possible mutations for the experiment + pattern: "possible_mutations.csv" + - versions: + - versions.yml: + type: file + description: File containing software versions + pattern: "versions.yml" + +authors: + - "@BenjaminWehnert1008" + - "@MaximilianStammnitz" diff --git a/modules/local/dmsanalysis/possible_mutations/templates/possible_mutations.R b/modules/local/dmsanalysis/possible_mutations/templates/possible_mutations.R new file mode 100644 index 0000000..13a0702 --- /dev/null +++ b/modules/local/dmsanalysis/possible_mutations/templates/possible_mutations.R @@ -0,0 +1,197 @@ +#!/usr/bin/env Rscript + +# ------------------------------------------------------------------------------ +# Script: Generate Programmed Codon Variants +# Description: Generates all possible programmed codon mutations for a given +# wild-type sequence based on a specified mutagenesis strategy. +# Input: +# - wt_seq_input: Wild-type sequence (string or path to FASTA file). +# - start_stop_pos: Target sequence range format "start-stop". +# - mutagenesis_type: Strategy ('nnk', 'nns', 'nnh', 'nnn', 'nnk_nns', 'nnk_nns_nnh', 'custom'). +# - custom_codon_library_path: Path to custom library. Automatically detects +# if the file is a global list ("AAA, AAC...") or a position-wise CSV +# (requires a "Position" header). +# - output_file: Desired name/path for the output CSV. +# Output: A CSV file containing all possible mutated codons per position. +# ------------------------------------------------------------------------------ + +suppressMessages(library(Biostrings)) +suppressMessages(library(methods)) + +generate_possible_variants <- function(wt_seq_input, start_stop_pos, mutagenesis_type, + custom_codon_library_path, output_file) { + + # Parse the start and stop positions from the input format "start-stop" + positions <- unlist(strsplit(start_stop_pos, "-")) + start_pos <- as.numeric(positions[1]) + stop_pos <- as.numeric(positions[2]) + + # Load sequence from file or process as a direct string + if (file.exists(wt_seq_input)) { + seq_data <- Biostrings::readDNAStringSet(filepath = wt_seq_input) + wt_seq <- seq_data[[1]] + } else { + wt_seq <- Biostrings::DNAString(wt_seq_input) + } + + # Extract the target coding sequence + coding_seq <- Biostrings::subseq(wt_seq, start = start_pos, end = stop_pos) + coding_seq <- as.character(coding_seq) + + # Predefined codon dictionaries + nnk_codons <- c('AAG', 'AAT', 'ATG', 'ATT', 'AGG', 'AGT', 'ACG', 'ACT', 'TAG', 'TAT', 'TTG', 'TTT', 'TGG', 'TGT', 'TCG', 'TCT', 'GAG', 'GAT', 'GTG', 'GTT', 'GGG', 'GGT', 'GCG', 'GCT', 'CAG', 'CAT', 'CTG', 'CTT', 'CGG', 'CGT', 'CCG', 'CCT') + nns_codons <- c('AAG', 'AAC', 'ATG', 'ATC', 'AGG', 'AGC', 'ACG', 'ACC', 'TAG', 'TAC', 'TTG', 'TTC', 'TGG', 'TGC', 'TCG', 'TCC', 'GAG', 'GAC', 'GTG', 'GTC', 'GGG', 'GGC', 'GCG', 'GCC', 'CAG', 'CAC', 'CTG', 'CTC', 'CGG', 'CGC', 'CCG', 'CCC') + nnh_codons <- c('AAA', 'AAC', 'AAT', 'ATA', 'ATC', 'ATT', 'AGA', 'AGC', 'AGT', 'ACA', 'ACC', 'ACT', 'TAA', 'TAC', 'TAT', 'TTA', 'TTC', 'TTT', 'TGA', 'TGC', 'TGT', 'TCA', 'TCC', 'TCT', 'GAA', 'GAC', 'GAT', 'GTA', 'GTC', 'GTT', 'GGA', 'GGC', 'GGT', 'GCA', 'GCC', 'GCT', 'CAA', 'CAC', 'CAT', 'CTA', 'CTC', 'CTT', 'CGA', 'CGC', 'CGT', 'CCA', 'CCC', 'CCT') + nnn_codons <- c('AAA', 'AAC', 'AAG', 'AAT', 'ATA', 'ATC', 'ATG', 'ATT', 'AGA', 'AGC', 'AGG', 'AGT', 'ACA', 'ACC', 'ACG', 'ACT', 'TAA', 'TAC', 'TAG', 'TAT', 'TTA', 'TTC', 'TTG', 'TTT', 'TGA', 'TGC', 'TGG', 'TGT', 'TCA', 'TCC', 'TCG', 'TCT', 'GAA', 'GAC', 'GAG', 'GAT', 'GTA', 'GTC', 'GTG', 'GTT', 'GGA', 'GGC', 'GGG', 'GGT', 'GCA', 'GCC', 'GCG', 'GCT', 'CAA', 'CAC', 'CAG', 'CAT', 'CTA', 'CTC', 'CTG', 'CTT', 'CGA', 'CGC', 'CGG', 'CGT', 'CCA', 'CCC', 'CCG', 'CCT') + + # -------------------------------------------------------------------------- + # Custom Library Parsing with Auto-Detection + # -------------------------------------------------------------------------- + is_position_wise <- FALSE + position_lookup <- list() + custom_codons <- NULL + + if (mutagenesis_type == "custom") { + if (!file.exists(custom_codon_library_path) || is.null(custom_codon_library_path)) { + stop("Custom codons file must be provided and valid when using 'custom' mutagenesis_type.") + } + + # Auto-detect format by inspecting the first line + first_line <- readLines(custom_codon_library_path, n = 1) + + if (grepl("Position", first_line, ignore.case = TRUE)) { + # Format 1: Position-wise CSV file + is_position_wise <- TRUE + + # Read line-by-line instead of read.csv to avoid strict column matching errors + lines <- readLines(custom_codon_library_path) + + # Loop through lines, skipping the header (index 1) + for (line in lines[-1]) { + # Skip empty lines + if (trimws(line) == "") next + + # Split the line by commas + parts <- trimws(unlist(strsplit(line, ","))) + + # The first part is the position, everything else are the codons + pos_idx <- parts[1] + codon_vec <- parts[-1] + + # Remove any accidental empty strings (e.g., from trailing commas) + codon_vec <- codon_vec[codon_vec != ""] + + position_lookup[[pos_idx]] <- codon_vec + } + } else { + # Format 2: Global comma-separated list (Legacy compatibility) + custom_codons <- unlist(strsplit(readLines(custom_codon_library_path), ",")) + custom_codons <- trimws(custom_codons) + } + } + + # Helper function to split a DNA sequence into nucleotide triplets + split_into_codons <- function(seq) { + # Note: Double escaping is required for Perl regular expressions here + return(strsplit(seq, "(?<=.{3})", perl = TRUE)[[1]]) + } + + wt_codons <- split_into_codons(coding_seq) + + # Initialize dataframe to store final variant results + # Note: \$ escaping is maintained for Nextflow compatibility + result <- data.frame(Codon_Number = integer(), wt_codon = character(), Variant = character(), stringsAsFactors = FALSE) + + # Helper function to determine the target codon list per position + get_codon_list <- function(wt_codon, codon_index) { + if (mutagenesis_type == "nnk") { + return(nnk_codons) + } else if (mutagenesis_type == "nns") { + return(nns_codons) + } else if (mutagenesis_type == "nnh") { + return(nnh_codons) + } else if (mutagenesis_type == "nnn") { + return(nnn_codons) + } else if (mutagenesis_type == "nnk_nns") { + if (substr(wt_codon, 3, 3) == "T") return(nns_codons) else return(nnk_codons) + } else if (mutagenesis_type == "nnk_nns_nnh") { + if (substr(wt_codon, 3, 3) == "T") { + return(nns_codons) + } else if (substr(wt_codon, 3, 3) == "G"){ + return(nnh_codons) + } else { + return(nnk_codons) + } + } else if (mutagenesis_type == "custom") { + if (is_position_wise) { + idx_str <- as.character(codon_index) + if (!is.null(position_lookup[[idx_str]])) { + return(position_lookup[[idx_str]]) + } else { + return(NULL) # Skip positions not explicitly defined in the CSV + } + } else { + return(custom_codons) + } + } else { + stop("Invalid mutagenesis_type. Choose from 'nnk', 'nns', 'nnh', 'nnn', 'nnk_nns', 'nnk_nns_nnh', or 'custom'.") + } + } + + # Iterate over each wild-type codon to assign programmed variants + for (i in seq_along(wt_codons)) { + wt_codon <- wt_codons[i] + codon_list <- get_codon_list(wt_codon, i) + + # Skip iteration if no custom codons were assigned to this specific position + if (is.null(codon_list)) next + + # Filter out the wild-type codon from the mutation list + possible_variants <- codon_list[codon_list != wt_codon] + + for (variant in possible_variants) { + # Note: \$ escaping is maintained for Nextflow compatibility + result <- rbind(result, data.frame(Codon_Number = i, wt_codon = wt_codon, Variant = variant, stringsAsFactors = FALSE)) + } + } + + write.csv(result, output_file, row.names = FALSE) +} + +# ------------------------------------------------------------------------------ +# Main Execution Block (Nextflow variable substitution) +# ------------------------------------------------------------------------------ + +# Replaces bash if/else logic. If Nextflow omits the optional file, +# it passes "/NULL", which R translates to an actual NULL object. +custom_lib_arg <- if ("$custom_codon_library" == "/NULL") { + NULL +} else { + "$custom_codon_library" +} + +generate_possible_variants( + wt_seq_input = "$wt_seq", + start_stop_pos = "$pos_range", + mutagenesis_type = "$mutagenesis_type", + custom_codon_library_path = "$custom_codon_library", + output_file = "possible_mutations.csv" +) + +# ------------------------------------------------------------------------------ +# Versioning Generation +# ------------------------------------------------------------------------------ + +r_version <- strsplit(version[['version.string']], ' ')[[1]][3] +biostrings_version <- as.character(packageVersion("Biostrings")) + +f <- file("versions.yml", "w") +writeLines( + c( + '"${task.process}":', + paste(' r-base:', r_version), + paste(' biostrings:', biostrings_version) + ), + f +) +close(f) diff --git a/modules/local/dmsanalysis/process_variant_counts/environment.yml b/modules/local/dmsanalysis/process_variant_counts/environment.yml new file mode 100644 index 0000000..197283a --- /dev/null +++ b/modules/local/dmsanalysis/process_variant_counts/environment.yml @@ -0,0 +1,15 @@ +channels: + - conda-forge + - bioconda +dependencies: + - bioconda::bioconductor-biostrings=2.78.0 + - conda-forge::r-base=4.5.1 + - conda-forge::r-biocmanager=1.30.27 + - conda-forge::r-dplyr=1.1.4 + - conda-forge::r-ggplot2=4.0.2 + - conda-forge::r-reshape2=1.4.4 + - conda-forge::r-scales=1.4.0 + - conda-forge::r-stringr=1.6.0 + - conda-forge::r-tidyr=1.3.1 + - conda-forge::r-tidyverse=2.0.0 + - conda-forge::r-zoo=1.8_15 diff --git a/modules/local/dmsanalysis/process_variant_counts/main.nf b/modules/local/dmsanalysis/process_variant_counts/main.nf new file mode 100644 index 0000000..c8a6347 --- /dev/null +++ b/modules/local/dmsanalysis/process_variant_counts/main.nf @@ -0,0 +1,41 @@ +process DMSANALYSIS_PROCESS_VARIANT_COUNTS { + tag "$meta.id" + label 'process_single' + + conda "${moduleDir}/environment.yml" + + container "${ workflow.containerEngine == 'singularity' + ? 'https://community-cr-prod.seqera.io/docker/registry/v2/blobs/sha256/73/73a72ec77725aeb67678a74228938fdd6827b669d01a8c96951b1a8ef96eeb0f/data' + : 'community.wave.seqera.io/library/bioconductor-biostrings_bioconductor-mutscan_r-base_r-biocmanager_pruned:c65036d76406f342' }" + + publishDir "${params.outdir}/intermediate_files", mode: 'copy' + + input: + tuple val(meta), path(variantCounts) + path possible_mutations + path aa_seq + val min_counts + + output: + tuple val(meta), + path("annotated_variantCounts.csv"), + path("variantCounts_filtered_by_library.csv"), + path("library_completed_variantCounts.csv"), + path("variantCounts_for_heatmaps.csv"), + emit: processed_variantCounts + + path "versions.yml", emit: versions + + when: + task.ext.when == null || task.ext.when + + script: + template 'process_variant_counts.R' + + stub: + """ + touch annotated_variantCounts.csv variantCounts_filtered_by_library.csv library_completed_variantCounts.csv variantCounts_for_heatmaps.csv + echo "DMSANALYSIS_PROCESS_VARIANT_COUNTS:" > versions.yml + echo " stub-version: 0.0.0" >> versions.yml + """ +} diff --git a/modules/local/dmsanalysis/process_variant_counts/meta.yml b/modules/local/dmsanalysis/process_variant_counts/meta.yml new file mode 100644 index 0000000..c72060b --- /dev/null +++ b/modules/local/dmsanalysis/process_variant_counts/meta.yml @@ -0,0 +1,69 @@ +name: "dmsanalysis_process_variant_counts" +description: "Processes the raw variant count table, filters it against expected libraries, and prepares data for downstream visualization and fitness calculation." +keywords: + - deep mutational scanning + - variant counting + - variant filtering + - dms +tools: + - "mutscan": + description: "R package for analysis of deep mutational scanning data" + homepage: "https://bioconductor.org/packages/release/bioc/html/mutscan.html" + documentation: "https://bioconductor.org/packages/release/bioc/manuals/mutscan/man/mutscan.pdf" + tool_dev_url: "https://github.com/csoneson/mutscan" + doi: "10.1186/s12859-023-05187-y" + licence: ["GPL-3.0-or-later"] + +input: + - - meta: + type: map + description: | + Groovy Map containing sample information + e.g. `[ id:'test', single_end:false ]` + - variantCounts: + type: file + description: TSV file containing the raw variant counts + pattern: "*.{tsv,csv}" + - - possible_mutations: + type: file + description: CSV file containing all theoretically possible mutations based on library design + pattern: "*.{csv}" + - - aa_seq: + type: file + description: FASTA or text file containing the reference amino acid sequence + - - min_counts: + type: integer + description: Minimum count threshold to consider a variant valid + +output: + - processed_variantCounts: + - meta: + type: map + description: | + Groovy Map containing sample information + e.g. `[ id:'test', single_end:false ]` + - annotated_variantCounts.csv: + type: file + description: Raw variant counts annotated with mutation details + pattern: "annotated_variantCounts.csv" + - variantCounts_filtered_by_library.csv: + type: file + description: Variant counts filtered to include only those present in the provided library + pattern: "variantCounts_filtered_by_library.csv" + - library_completed_variantCounts.csv: + type: file + description: Full library table including zero-count variants + pattern: "library_completed_variantCounts.csv" + - variantCounts_for_heatmaps.csv: + type: file + description: Processed counts formatted specifically for heatmap visualization + pattern: "variantCounts_for_heatmaps.csv" + - versions: + - versions.yml: + type: file + description: File containing software versions + pattern: "versions.yml" + +authors: + - "@BenjaminWehnert1008" + - "@MaximilianStammnitz" diff --git a/modules/local/dmsanalysis/process_variant_counts/templates/process_variant_counts.R b/modules/local/dmsanalysis/process_variant_counts/templates/process_variant_counts.R new file mode 100644 index 0000000..e1871b3 --- /dev/null +++ b/modules/local/dmsanalysis/process_variant_counts/templates/process_variant_counts.R @@ -0,0 +1,364 @@ +#!/usr/bin/env Rscript + +# four parts condensed into one R script + +######### +# process raw counts +######### + +# input: counts variantcounts tsv file, output_path +# output: csv with column names. Creates additional counts_per_cov column. Fills pos_mut column for synonymous mutations. Sorted out variants that have mutations, but do not show up in the specifying columns -> was affecting roughly 30 low-count variants out of over 15000 in Taylor's data + +library("dplyr") +process_raw_counts <- function(counts_file_path, output_csv_path) { + + # Set the column names + colnames <- c("counts", "cov", "mean_length_variant_reads", "varying_bases", + "base_mut", "varying_codons", "codon_mut", "aa_mut", "pos_mut") + + # Read the variant-count file into a data frame + counts_raw <- read.table(counts_file_path, sep = "\\t", header = FALSE, fill = TRUE, col.names = colnames) + + # Filter out rows where 'aa_mut' is empty or NA + counts_raw <- counts_raw[!(counts_raw\$aa_mut == "" | is.na(counts_raw\$aa_mut)), ] + + # Handle synonymous mutations: where aa_mut starts with "S" and pos_mut is either NA or "" + counts_raw <- counts_raw %>% + rowwise() %>% + mutate( + pos_mut = ifelse( + (is.na(pos_mut) | pos_mut == "") & grepl("^S:", aa_mut), + # Construct the new 'pos_mut' entry for synonymous mutations + paste0( + sub("S:([A-Z])>[A-Z]", "\\\\1", aa_mut), # Get the original amino acid from 'aa_mut' + sub("^(\\\\d+):.*", "\\\\1", codon_mut), # Get the position from 'codon_mut' + sub("S:[A-Z]>([A-Z])", "\\\\1", aa_mut) # Get the mutated amino acid from 'aa_mut' + ), + pos_mut # Keep the existing 'pos_mut' if it's not NA or "" + ) + ) %>% + ungroup() %>% + mutate(counts_per_cov = counts / cov) + + # Write the cleaned data frame to a CSV file + write.csv(counts_raw, file = output_csv_path, row.names = FALSE) +} + +##### +# run function +##### +process_raw_counts( + counts_file_path = "$variantCounts", + output_csv_path = "annotated_variantCounts.csv" +) + + + + + + + + +######### +# filter counts by codon library +######### + +# Input: pre_processed_raw_counts_path, mutation library .csv path (former "possible_NNK_mutations.csv"), output_path +# Output: counts table filtered for only single-codon mutations that are part of the library + +library("dplyr") +library("stringr") + +filter_counts_by_codon_library <- function(counts_file_path, codon_library_path, output_file_path) { + # Load the variant-count table from the provided file path + counts_table <- read.csv(counts_file_path) + + # Load the codon library from the provided .csv file + codon_library <- read.csv(codon_library_path) + + # Ensure the codon library has the expected columns + if (!all(c("Codon_Number", "wt_codon", "Variant") %in% colnames(codon_library))) { + stop("Codon library must contain columns 'Codon_Number', 'wt_codon', and 'Variant'.") + } + + # Filter the variant-count table + filtered_counts <- counts_table %>% + filter(varying_codons == 1) %>% # Keep rows with single-codon mutations + rowwise() %>% + filter({ + # Extract the position and mutated codon + codon_position <- as.numeric(sub(":.*", "", codon_mut)) # Extract position before ':' + mutated_codon <- sub(".*>", "", codon_mut) # Extract codon after '>' + + # Check if the position and codon are valid + is_in_library <- any( + codon_library\$Codon_Number == codon_position & + (codon_library\$Variant == mutated_codon | # Check Variant column + codon_library\$wt_codon == mutated_codon) # Check wt_codon column + ) + is_in_library + }) %>% + ungroup() %>% + # Apply additional filtering based on mutation distances + rowwise() %>% + filter({ + # Split base_mut into individual mutations + mutations <- unlist(strsplit(base_mut, ",\\\\s*")) # Splits by comma and removes extra spaces + # Extract numeric positions from each mutation string + positions <- as.numeric(str_extract(mutations, "^[0-9]+")) + + # Calculate the distance between the first and last position + distance <- max(positions, na.rm = TRUE) - min(positions, na.rm = TRUE) + + # Keep rows where the distance is <= 2 + distance <= 2 + }) %>% + ungroup() + + # Write the filtered variant-count table to the output file path + write.csv(filtered_counts, file = output_file_path, row.names = FALSE) +} + +##### +# run function +##### +filter_counts_by_codon_library( + counts_file_path = "annotated_variantCounts.csv", + codon_library_path = "$possible_mutations", + output_file_path = "variantCounts_filtered_by_library.csv" +) + + + + +######### +# complete filtered counts +######### + +# input: NNK_codon_library_filtered_counts.csv-path, prefiltered_counts.csv-path (containing only NNK mutations), output_folder-path +# output: completed counts_file with all possible variants (even if not measured in sequencing) -> NA in counts and counts_per_cov to 0.0000001 to deal with log-scale in following calculations + +library(dplyr) +library(Biostrings) # Required for codon-to-amino-acid translation + +# Function to calculate Hamming distance (varying_bases) +hamming_distance <- function(wt_codon, variant_codon) { + sum(strsplit(wt_codon, "")[[1]] != strsplit(variant_codon, "")[[1]]) +} + +# Function to get amino acid from codon +get_amino_acid <- function(codon) { + codon_table <- GENETIC_CODE + aa <- codon_table[[toupper(codon)]] + if (is.null(aa)) { + return(NA) # Handle cases where codon is not valid + } + return(aa) +} + +# Function to calculate mutation type (aa_mut) and pos_mut +mutation_details <- function(wt_codon, variant_codon, codon_number) { + wt_aa <- get_amino_acid(wt_codon) + variant_aa <- get_amino_acid(variant_codon) + + # If amino acids are different, it's a missense mutation; otherwise, synonymous + if (wt_aa != variant_aa) { + mutation_type <- "M" # Missense mutation + } else { + mutation_type <- "S" # Synonymous mutation + } + + # aa_mut: Type of mutation and amino acid changes (e.g., M:D>S) + aa_mut <- paste0(mutation_type, ":", wt_aa, ">", variant_aa) + + # pos_mut: Wild-type AA, codon position, mutated AA (e.g., D2Q) + pos_mut <- paste0(wt_aa, codon_number, variant_aa) + + return(list(aa_mut = aa_mut, pos_mut = pos_mut)) +} + +complete_prefiltered_counts <- function(possible_nnk_path, prefiltered_counts_path, output_file_path) { + + # Load the possible NNK mutations CSV + possible_nnk <- read.csv(possible_nnk_path) + + # Load the prefiltered variant-count CSV + prefiltered_counts <- read.csv(prefiltered_counts_path) + + # Create codon_mut column in possible_NNK_mutations in the format 'Codon_Number:wt_codon>Variant' + possible_nnk <- possible_nnk %>% + mutate(codon_mut = paste0(Codon_Number, ":", wt_codon, ">", Variant)) + + # Merge both dataframes based on the codon_mut column (full join to include all) + merged_data <- full_join(prefiltered_counts, possible_nnk, by = "codon_mut") + + # Fill missing values in counts_per_cov and counts with 0.0000001 + merged_data <- merged_data %>% + mutate(counts_per_cov = ifelse(is.na(counts_per_cov), 0.0000001, counts_per_cov), + counts = ifelse(is.na(counts), 0.000001, counts)) + + # Calculate Hamming distance (varying_bases) and mutation details (aa_mut, pos_mut) + merged_data <- merged_data %>% + rowwise() %>% + mutate(varying_bases = hamming_distance(wt_codon, Variant), + mutation_info = list(mutation_details(wt_codon, Variant, Codon_Number))) %>% + mutate(aa_mut = mutation_info\$aa_mut, # Extract aa_mut + pos_mut = mutation_info\$pos_mut) %>% # Extract pos_mut + ungroup() %>% + select(-mutation_info) # Remove the temporary list column + + # Save the merged data to a new CSV file + write.csv(merged_data, file = output_file_path, row.names = FALSE) +} + +##### +# run function +##### +complete_prefiltered_counts( + possible_nnk_path = "$possible_mutations", + prefiltered_counts_path = "variantCounts_filtered_by_library.csv", + output_file_path = "library_completed_variantCounts.csv" +) + + + + + + + + + +######### +# prepare counts data for counts heatmap +######### + +# input: prefiltered variant-count path (filtered for codon library), aa-seq file path, output path, threshold (for minimum counts to recognize variant) +# output: csv file serving as basis for counts_per_cov_heatmap function + +suppressMessages(library(dplyr)) +suppressMessages(library(ggplot2)) +suppressMessages(library(tidyr)) +suppressMessages(library(reshape2)) +suppressMessages(library(scales)) + +prepare_counts_data_for_counts_heatmaps <- function(counts_file_path, aa_seq_file_path, output_csv_path, threshold = 3) { + # Load the raw variant-count data + raw_counts <- read.table(counts_file_path, sep = ",", header = TRUE) + + # Read the wild-type amino acid sequence from the text file + wt_seq <- readLines(aa_seq_file_path) + wt_seq <- unlist(strsplit(wt_seq, "")) # Split the sequence into individual amino acids + + # Summarize counts-per-cov for each unique aa mutation in pos_mut + aggregated_data <- raw_counts %>% + group_by(pos_mut) %>% + summarize(total_counts_per_cov = sum(counts_per_cov, na.rm = TRUE), + total_counts = sum(counts, na.rm = TRUE)) # Also sum the counts + + # Extract the wild-type position and mutations from 'pos_mut' + aggregated_data <- aggregated_data %>% + mutate( + wt_aa = sub("(\\\\D)(\\\\d+)(\\\\D)", "\\\\1", pos_mut), # Wild-type amino acid (e.g., S) + position = as.numeric(sub("(\\\\D)(\\\\d+)(\\\\D)", "\\\\2", pos_mut)), # Position (e.g., 3) + mut_aa = sub("(\\\\D)(\\\\d+)(\\\\D)", "\\\\3", pos_mut) # Mutant amino acid (e.g., R) + ) + + # Replace 'X' with '*', indicating the stop codon + aggregated_data <- aggregated_data %>% + mutate(mut_aa = ifelse(mut_aa == "X", "*", mut_aa)) + + # Replace 'X' with '*' in the wild-type amino acid sequence as well + wt_seq <- ifelse(wt_seq == "X", "*", wt_seq) + + # Define all 20 standard amino acids and the stop codon "*" + all_amino_acids <- c("A", "C", "D", "E", "F", "G", "H", "I", "K", "L", + "M", "N", "P", "Q", "R", "S", "T", "V", "W", "Y", "*") + + # Create a list of all positions in the wild-type sequence + all_positions <- 1:length(wt_seq) + + # Create a complete grid of all possible combinations of positions and amino acids + complete_data <- expand.grid(mut_aa = all_amino_acids, position = all_positions) + + # Merge the summarized data with the complete grid (filling missing entries with 0) + heatmap_data <- complete_data %>% + left_join(aggregated_data, by = c("mut_aa", "position")) %>% + mutate(total_counts_per_cov = ifelse(is.na(total_counts_per_cov), 0, total_counts_per_cov), + wt_aa = wt_seq[position]) # Assign the wild-type amino acid + + # Set variants with counts < threshold to NA + heatmap_data <- heatmap_data %>% + mutate( + total_counts_per_cov = ifelse(total_counts < threshold, NA, total_counts_per_cov), + total_counts = ifelse(total_counts < threshold, NA, total_counts) + ) + + # Fill pos_mut column + heatmap_data <- heatmap_data %>% + mutate( + pos_mut = ifelse(is.na(pos_mut), + paste0(wt_aa, position, mut_aa), + pos_mut) + ) + + # Save the aggregated data to a CSV file + write.csv(heatmap_data, file = output_csv_path, row.names = FALSE) + print(paste("Aggregated data saved to:", output_csv_path)) +} + + +##### +# run function +##### +prepare_counts_data_for_counts_heatmaps( + counts_file_path = "variantCounts_filtered_by_library.csv", + aa_seq_file_path = "$aa_seq", + output_csv_path = "variantCounts_for_heatmaps.csv", + threshold = $min_counts +) + + + + + + + +#### +# create versions.yml +#### +r_version <- strsplit(version[['version.string']], ' ')[[1]][3] +dplyr_version <- as.character(packageVersion("dplyr")) +Biostrings_version <- as.character(packageVersion("Biostrings")) +stringr_version <- as.character(packageVersion("stringr")) +ggplot2_version <- as.character(packageVersion("ggplot2")) +tidyr_version <- as.character(packageVersion("tidyr")) +reshape2_version <- as.character(packageVersion("reshape2")) +scales_version <- as.character(packageVersion("scales")) + + +if (is.null(r_version)) r_version <- "unknown" +if (length(dplyr_version) == 0) dplyr_version <- "unknown" +if (length(Biostrings_version) == 0) Biostrings_version <- "unknown" +if (length(stringr_version) == 0) stringr_version <- "unknown" +if (length(ggplot2_version) == 0) ggplot2_version <- "unknown" +if (length(tidyr_version) == 0) tidyr_version <- "unknown" +if (length(reshape2_version) == 0) reshape2_version <- "unknown" +if (length(scales_version) == 0) scales_version <- "unknown" + + +f <- file("versions.yml", "w") +writeLines( + c( + '"${task.process}":', + paste(' r-base:', r_version), + paste(' r-dplyr:', dplyr_version), + paste(' r-Biostrings:', Biostrings_version), + paste(' r-stringr:', stringr_version), + paste(' r-ggplot2:', ggplot2_version), + paste(' r-tidyr:', tidyr_version), + paste(' r-reshape2:', reshape2_version), + paste(' r-scales:', scales_version) + ), + f +) +close(f) diff --git a/modules/local/fitness/counts_to_fitness/environment.yml b/modules/local/fitness/counts_to_fitness/environment.yml new file mode 100644 index 0000000..197283a --- /dev/null +++ b/modules/local/fitness/counts_to_fitness/environment.yml @@ -0,0 +1,15 @@ +channels: + - conda-forge + - bioconda +dependencies: + - bioconda::bioconductor-biostrings=2.78.0 + - conda-forge::r-base=4.5.1 + - conda-forge::r-biocmanager=1.30.27 + - conda-forge::r-dplyr=1.1.4 + - conda-forge::r-ggplot2=4.0.2 + - conda-forge::r-reshape2=1.4.4 + - conda-forge::r-scales=1.4.0 + - conda-forge::r-stringr=1.6.0 + - conda-forge::r-tidyr=1.3.1 + - conda-forge::r-tidyverse=2.0.0 + - conda-forge::r-zoo=1.8_15 diff --git a/modules/local/fitness/counts_to_fitness/main.nf b/modules/local/fitness/counts_to_fitness/main.nf new file mode 100644 index 0000000..1423e99 --- /dev/null +++ b/modules/local/fitness/counts_to_fitness/main.nf @@ -0,0 +1,32 @@ +process COUNTS_TO_FITNESS { + tag "$meta.id" + label 'process_single' + + conda "${moduleDir}/environment.yml" + + container "${ workflow.containerEngine == 'singularity' + ? 'https://community-cr-prod.seqera.io/docker/registry/v2/blobs/sha256/73/73a72ec77725aeb67678a74228938fdd6827b669d01a8c96951b1a8ef96eeb0f/data' + : 'community.wave.seqera.io/library/bioconductor-biostrings_bioconductor-mutscan_r-base_r-biocmanager_pruned:c65036d76406f342' }" + + input: + tuple val(meta), path(variantCounts_filtered_by_library) + path wt_seq + val pos_range + + output: + tuple val(meta), path("${meta.id}_fitness_input.tsv"), emit: fitness_input + path "versions.yml", emit: versions + + when: + task.ext.when == null || task.ext.when + + script: + template 'counts_to_fitness.R' + + stub: + """ + touch ${meta.id}_fitness_input.tsv + echo "COUNTS_TO_FITNESS:" > versions.yml + echo " stub-version: 0.0.0" >> versions.yml + """ +} diff --git a/modules/local/fitness/counts_to_fitness/meta.yml b/modules/local/fitness/counts_to_fitness/meta.yml new file mode 100644 index 0000000..e0aae49 --- /dev/null +++ b/modules/local/fitness/counts_to_fitness/meta.yml @@ -0,0 +1,57 @@ +name: "counts_to_fitness" +description: Reformats the library-filtered variant count table into a standardized TSV compatible with downstream fitness calculation tools, ensuring mutation nomenclature is consistent with the reference sequence. +keywords: + - deep mutational scanning + - dms + - variant counting + - format conversion + - fitness +tools: + - "mutscan": + description: "R package for analysis of deep mutational scanning data" + homepage: "https://bioconductor.org/packages/release/bioc/html/mutscan.html" + documentation: "https://bioconductor.org/packages/release/bioc/manuals/mutscan/man/mutscan.pdf" + tool_dev_url: "https://github.com/csoneson/mutscan" + doi: "10.1186/s12859-023-05187-y" + licence: ["GPL-3.0-or-later"] + - "bioconductor-biostrings": + description: "Efficient manipulation of biological strings" + homepage: "https://bioconductor.org/packages/Biostrings" + licence: ["Artistic-2.0"] + +input: + - - meta: + type: map + description: | + Groovy Map containing sample information + e.g. `[ id:'test', single_end:false ]` + - variantCounts_filtered_by_library: + type: file + description: CSV file containing variant counts already filtered against the design library + pattern: "*.{csv}" + - - wt_seq: + type: file + description: FASTA file containing the wild-type DNA reference sequence + pattern: "*.{fasta,fa}" + - - pos_range: + type: string + description: Start and stop codon positions (ORF) in the format 'start-stop' + +output: + - fitness_input: + - meta: + type: map + description: Groovy Map containing sample information + - "${meta.id}_fitness_input.tsv": + type: file + description: A standardized TSV file ready for input into fitness calculation modules + pattern: "*_fitness_input.tsv" + - versions: + - versions.yml: + type: file + description: File containing software versions + pattern: "versions.yml" + +authors: + - "@BenjaminWehnert1008" + - "@MaximilianStammnitz" diff --git a/modules/local/fitness/counts_to_fitness/templates/counts_to_fitness.R b/modules/local/fitness/counts_to_fitness/templates/counts_to_fitness.R new file mode 100644 index 0000000..b234058 --- /dev/null +++ b/modules/local/fitness/counts_to_fitness/templates/counts_to_fitness.R @@ -0,0 +1,103 @@ +#!/usr/bin/env Rscript + +suppressMessages(library(Biostrings)) + +generate_fitness_input <- function(wt_seq_path, counts_file, pos_range, output_file_path) { + # Parse the position range + positions <- unlist(strsplit(pos_range, "-")) + start_pos <- as.numeric(positions[1]) + stop_pos <- as.numeric(positions[2]) + + # Load the wild-type sequence + seq_data <- Biostrings::readDNAStringSet(filepath = wt_seq_path) + wt_seq <- seq_data[[1]] # Extract the sequence + wt_seq <- subseq(wt_seq, start = start_pos, end = stop_pos) + + # Convert wt_seq to a character string + wt_seq <- as.character(wt_seq) + + # Split the wild-type sequence into codons (groups of 3 bases) + wt_codons <- substring(wt_seq, seq(1, nchar(wt_seq), 3), seq(3, nchar(wt_seq), 3)) + + # Helper function to process variant-count CSVs into count data + process_counts_file <- function(counts_csv) { + # Load the input variant-count CSV file + counts_data <- read.csv(counts_csv, stringsAsFactors = FALSE) + + # Initialize a data frame for results + results <- data.frame( + nt_seq = character(), + count = numeric(), + stringsAsFactors = FALSE + ) + + # Iterate over each row in the input data + for (i in 1:nrow(counts_data)) { + # Extract the mutation info + codon_mut <- counts_data\$codon_mut[i] + + if("counts_corrected" %in% colnames(counts_data)){ + counts <- counts_data\$counts_corrected[i] + }else{ + counts <- counts_data\$counts[i] + } + + # Create a mutable copy of the wild-type codons + mutated_codons <- wt_codons + + # Apply the mutation + mutations <- strsplit(codon_mut, ", ")[[1]] + for (mutation in mutations) { + codon_position <- as.numeric(sub(":.*", "", mutation)) + new_codon <- sub(".*>", "", mutation) + # Replace the codon at the specified position + mutated_codons[codon_position] <- new_codon + } + + # Convert the mutated codons back to a sequence string + mutated_seq_string <- paste(mutated_codons, collapse = "") + + # Add the result to the data frame + results <- rbind(results, data.frame(nt_seq = mutated_seq_string, count = counts)) + } + + return(results) + } + + # Process the variant-count file + cat("Processing variant-count file...\\n") + processed_data <- process_counts_file(counts_file) + + # Write the processed data to a file without column names + write.table(processed_data, file = output_file_path, sep = "\\t", row.names = FALSE, col.names = FALSE, quote = FALSE) +} + +##### +# run function +##### +generate_fitness_input( + wt_seq_path = "$wt_seq", + counts_file = "$variantCounts_filtered_by_library", + pos_range = "$pos_range", + output_file_path = "${meta.id}_fitness_input.tsv" +) + +##### +# create versions.yml +##### +r_version <- strsplit(version[['version.string']], ' ')[[1]][3] +Biostrings_version <- as.character(packageVersion("Biostrings")) + +if (is.null(r_version)) r_version <- "unknown" +if (length(Biostrings_version) == 0) Biostrings_version <- "unknown" + +f <- file("versions.yml", "w") +writeLines( + c( + '"${task.process}":', + paste(' r-base:', r_version), + paste(' r-Biostrings:', Biostrings_version) + ), + f +) +close(f) diff --git a/modules/local/fitness/find_synonymous_mutation/environment.yml b/modules/local/fitness/find_synonymous_mutation/environment.yml new file mode 100644 index 0000000..197283a --- /dev/null +++ b/modules/local/fitness/find_synonymous_mutation/environment.yml @@ -0,0 +1,15 @@ +channels: + - conda-forge + - bioconda +dependencies: + - bioconda::bioconductor-biostrings=2.78.0 + - conda-forge::r-base=4.5.1 + - conda-forge::r-biocmanager=1.30.27 + - conda-forge::r-dplyr=1.1.4 + - conda-forge::r-ggplot2=4.0.2 + - conda-forge::r-reshape2=1.4.4 + - conda-forge::r-scales=1.4.0 + - conda-forge::r-stringr=1.6.0 + - conda-forge::r-tidyr=1.3.1 + - conda-forge::r-tidyverse=2.0.0 + - conda-forge::r-zoo=1.8_15 diff --git a/modules/local/fitness/find_synonymous_mutation/main.nf b/modules/local/fitness/find_synonymous_mutation/main.nf new file mode 100644 index 0000000..2ea78e6 --- /dev/null +++ b/modules/local/fitness/find_synonymous_mutation/main.nf @@ -0,0 +1,31 @@ +process FIND_SYNONYMOUS_MUTATION { + tag { sample.sample } + label 'process_single' + + conda "${moduleDir}/environment.yml" + + container "${ workflow.containerEngine == 'singularity' + ? 'https://community-cr-prod.seqera.io/docker/registry/v2/blobs/sha256/73/73a72ec77725aeb67678a74228938fdd6827b669d01a8c96951b1a8ef96eeb0f/data' + : 'community.wave.seqera.io/library/bioconductor-biostrings_bioconductor-mutscan_r-base_r-biocmanager_pruned:c65036d76406f342' }" + + input: + tuple val(sample), path(counts_merged) // from MERGE_COUNTS.out.merged_counts + path wt_fasta // broadcast singleton + val pos_range // "start-end", broadcast singleton + + output: + tuple val(sample), path("synonymous_wt.txt"), emit: synonymous_wt + path "versions.yml", emit: versions + + script: + template 'find_syn_mutation.R' + + stub: + """ + touch synonymous_wt.txt + cat <<-END_VERSIONS > versions.yml + "${task.process}": + r-base: "0.0.0" + END_VERSIONS + """ +} diff --git a/modules/local/fitness/find_synonymous_mutation/meta.yml b/modules/local/fitness/find_synonymous_mutation/meta.yml new file mode 100644 index 0000000..8ec3ab3 --- /dev/null +++ b/modules/local/fitness/find_synonymous_mutation/meta.yml @@ -0,0 +1,58 @@ +name: "find_synonymous_mutation" +description: Identifies synonymous mutations within the merged count table, prioritizing those with high counts (typically 2nt changes) to serve as a reference for normalizing fitness scores. +keywords: + - deep mutational scanning + - dms + - synonymous mutation + - fitness calculation + - normalization +tools: + - "mutscan": + description: "R package for analysis of deep mutational scanning data" + homepage: "https://bioconductor.org/packages/release/bioc/html/mutscan.html" + documentation: "https://bioconductor.org/packages/release/bioc/manuals/mutscan/man/mutscan.pdf" + tool_dev_url: "https://github.com/csoneson/mutscan" + doi: "10.1186/s12859-023-05187-y" + licence: ["GPL-3.0-or-later"] + - "bioconductor-biostrings": + description: "Memory efficient string containers, string matching algorithms, and other utilities, for fast manipulation of large biological sequences or sets of sequences" + homepage: "https://bioconductor.org/packages/Biostrings" + licence: ["Artistic-2.0"] + +input: + - - sample: + type: map + description: | + Groovy Map containing sample information + e.g. `[ id:'test' ]` + - counts_merged: + type: file + description: TSV/CSV file containing variant counts merged across samples or replicates + pattern: "*.{tsv,csv}" + - - wt_fasta: + type: file + description: FASTA file containing the wild-type reference DNA sequence + pattern: "*.{fasta,fa}" + - - pos_range: + type: string + description: Start and stop codon positions (ORF) in the format 'start-stop', e.g., '352-1383' + +output: + - synonymous_wt: + - sample: + type: map + description: | + Groovy Map containing sample information + - synonymous_wt.txt: + type: file + description: Text file identifying the chosen synonymous mutation to be used as a wild-type reference + pattern: "synonymous_wt.txt" + - versions: + - versions.yml: + type: file + description: File containing software versions + pattern: "versions.yml" + +authors: + - "@BenjaminWehnert1008" + - "@MaximilianStammnitz" diff --git a/modules/local/fitness/find_synonymous_mutation/templates/find_syn_mutation.R b/modules/local/fitness/find_synonymous_mutation/templates/find_syn_mutation.R new file mode 100644 index 0000000..a0ee07a --- /dev/null +++ b/modules/local/fitness/find_synonymous_mutation/templates/find_syn_mutation.R @@ -0,0 +1,119 @@ +#!/usr/bin/env Rscript + +suppressMessages(library(Biostrings)) + +# Pick a synonymous "WT substitute" for DiMSum normalization using a fixed coding window. +# Inputs: +# wt_fasta : path to FASTA (single WT sequence) +# counts_merged_tsv : path to merged counts (columns: nt_seq, input1..N, output1..M) +# pos_range : "start-end" (1-based, inclusive), e.g. "352-1383" +# Returns: +# character scalar: chosen nt sequence +# +# Preference: +# 1) AA-identical (fully synonymous) AND exactly 2 nt mismatches vs WT, both within ONE codon. +# 2) If none, AA-identical AND exactly 1 nt mismatch vs WT. +# 3) If more than one, pick highest mean of input counts. +# 4) If still none, stop with an error. + +pick_synonymous_wt_from_range <- function(wt_fasta, counts_merged_tsv, pos_range) { + ## ---- parse range ---- + pr <- strsplit(as.character(pos_range), "-", fixed = TRUE)[[1]] + if (length(pr) != 2L) stop("pos_range must be 'start-end', got: ", pos_range) + start_pos <- as.integer(pr[1]); end_pos <- as.integer(pr[2]) + if (is.na(start_pos) || is.na(end_pos) || start_pos < 1L || end_pos < start_pos) + stop("Invalid pos_range: ", pos_range) + + ## ---- WT window ---- + wt_set <- Biostrings::readDNAStringSet(wt_fasta) + if (length(wt_set) != 1L) stop("WT FASTA must contain exactly one sequence.") + wt_subseq <- Biostrings::subseq(wt_set[[1]], start = start_pos, end = end_pos) + wt_seq_chr <- as.character(wt_subseq) + wt_len <- nchar(wt_seq_chr) + if ((wt_len %% 3) != 0) stop("Provided window length is not divisible by 3: ", wt_len) + wt_aa <- Biostrings::translate(wt_subseq, if.fuzzy.codon = "X") + wt_chars <- strsplit(wt_seq_chr, "", fixed = TRUE)[[1]] + + ## ---- counts ---- + df <- utils::read.delim(counts_merged_tsv, sep = "\\t", header = TRUE, + stringsAsFactors = FALSE, check.names = FALSE) + if (!"nt_seq" %in% names(df)) stop("counts_merged_tsv must have a 'nt_seq' column.") + + df\$nt_seq <- toupper(df\$nt_seq) + keep_len <- nchar(df\$nt_seq) == wt_len + if (!any(keep_len)) stop("No sequences match WT window length (", wt_len, ").") + if (!all(keep_len)) df <- df[keep_len, , drop = FALSE] + + # input columns & mean (works with 1+ replicates) + input_cols <- grep("^input", names(df), value = TRUE) + if (length(input_cols) == 0L) stop("No input columns found (expect names starting with 'input').") + input_mat <- as.data.frame(lapply(df[, input_cols, drop = FALSE], function(x) as.numeric(as.character(x)))) + input_mean <- if (length(input_cols) == 1L) input_mat[[1]] else rowMeans(as.matrix(input_mat), na.rm = TRUE) + + ## ---- synonymous filter ---- + var_set <- Biostrings::DNAStringSet(df\$nt_seq) + var_aa <- Biostrings::translate(var_set, if.fuzzy.codon = "X") + syn_idx <- which(as.character(var_aa) == as.character(wt_aa)) + if (length(syn_idx) == 0L) stop("No fully-synonymous variants found relative to WT translation.") + + # helpers + mismatch_positions <- function(seq_nt_chars) which(seq_nt_chars != wt_chars) # 1-based positions + codon_index <- function(pos_vec) floor((pos_vec - 1L) / 3L) # 0-based codon bin + + # preference 1: exactly 2 mismatches, both within the same codon + cand_two_one <- Filter(function(i) { + vchars <- strsplit(df\$nt_seq[i], "", fixed = TRUE)[[1]] + pos <- mismatch_positions(vchars) + length(pos) == 2L && length(unique(codon_index(pos))) == 1L + }, syn_idx) + + choose_best <- function(idx_vec) idx_vec[ which.max(input_mean[idx_vec]) ] + + if (length(cand_two_one) > 0L) { + best_i <- choose_best(cand_two_one) + return(as.character(df\$nt_seq[best_i])) + } + + # preference 2 (fallback): exactly 1 mismatch (still synonymous) + cand_one <- Filter(function(i) { + vchars <- strsplit(df\$nt_seq[i], "", fixed = TRUE)[[1]] + length(mismatch_positions(vchars)) == 1L + }, syn_idx) + + if (length(cand_one) > 0L) { + best_i <- choose_best(cand_one) + return(as.character(df\$nt_seq[best_i])) + } + + stop("No suitable synonymous variant found: neither 2-in-1-codon nor 1-nt synonymous candidates present.") +} + +##### +# run function +##### +seq <- pick_synonymous_wt_from_range( + wt_fasta = "$wt_fasta", + counts_merged_tsv = "$counts_merged", + pos_range = "$pos_range" + ) +write(seq, file='synonymous_wt.txt') + +#### +# create versions.yml +#### +r_version <- strsplit(version[['version.string']], ' ')[[1]][3] +Biostrings_version <- as.character(packageVersion("Biostrings")) + +if (is.null(r_version)) r_version <- "unknown" +if (length(Biostrings_version) == 0) Biostrings_version <- "unknown" + +f <- file("versions.yml", "w") +writeLines( + c( + '"${task.process}":', + paste(' r-base:', r_version), + paste(' r-Biostrings:', Biostrings_version) + ), + f +) +close(f) diff --git a/modules/local/fitness/fitness_QC/environment.yml b/modules/local/fitness/fitness_QC/environment.yml new file mode 100644 index 0000000..197283a --- /dev/null +++ b/modules/local/fitness/fitness_QC/environment.yml @@ -0,0 +1,15 @@ +channels: + - conda-forge + - bioconda +dependencies: + - bioconda::bioconductor-biostrings=2.78.0 + - conda-forge::r-base=4.5.1 + - conda-forge::r-biocmanager=1.30.27 + - conda-forge::r-dplyr=1.1.4 + - conda-forge::r-ggplot2=4.0.2 + - conda-forge::r-reshape2=1.4.4 + - conda-forge::r-scales=1.4.0 + - conda-forge::r-stringr=1.6.0 + - conda-forge::r-tidyr=1.3.1 + - conda-forge::r-tidyverse=2.0.0 + - conda-forge::r-zoo=1.8_15 diff --git a/modules/local/fitness/fitness_QC/main.nf b/modules/local/fitness/fitness_QC/main.nf new file mode 100644 index 0000000..78420eb --- /dev/null +++ b/modules/local/fitness/fitness_QC/main.nf @@ -0,0 +1,31 @@ +process FITNESS_QC { + tag { sample.sample } + label 'process_single' + + conda "${moduleDir}/environment.yml" + + container "${ workflow.containerEngine == 'singularity' + ? 'https://community-cr-prod.seqera.io/docker/registry/v2/blobs/sha256/73/73a72ec77725aeb67678a74228938fdd6827b669d01a8c96951b1a8ef96eeb0f/data' + : 'community.wave.seqera.io/library/bioconductor-biostrings_bioconductor-mutscan_r-base_r-biocmanager_pruned:c65036d76406f342' }" + + input: + tuple val(sample), path(fitness_estimation_tsv) // from FITNESS_CALCULATION + + output: + tuple val(sample), path("fitness_estimation_count_correlation.pdf"), emit: counts_corr_pdf + tuple val(sample), path("fitness_estimation_fitness_correlation.pdf"), emit: fitness_corr_pdf + path "versions.yml", emit: versions + + script: + template 'fitness_QC.R' + + stub: + """ + touch fitness_estimation_count_correlation.pdf + touch fitness_estimation_fitness_correlation.pdf + cat > versions.yml <<'EOF' + FITNESS_PLOTS: + stub-version: "0.0.0" + EOF + """ +} diff --git a/modules/local/fitness/fitness_QC/meta.yml b/modules/local/fitness/fitness_QC/meta.yml new file mode 100644 index 0000000..309e225 --- /dev/null +++ b/modules/local/fitness/fitness_QC/meta.yml @@ -0,0 +1,54 @@ +name: "fitness_qc" +description: Generates quality control visualizations for fitness estimation results, including correlation plots of raw counts and calculated fitness scores to assess experimental reproducibility. +keywords: + - deep mutational scanning + - dms + - quality control + - correlation + - fitness +tools: + - "mutscan": + description: "R package for analysis of deep mutational scanning data" + homepage: "https://bioconductor.org/packages/release/bioc/html/mutscan.html" + documentation: "https://bioconductor.org/packages/release/bioc/manuals/mutscan/man/mutscan.pdf" + tool_dev_url: "https://github.com/csoneson/mutscan" + doi: "10.1186/s12859-023-05187-y" + licence: ["GPL-3.0-or-later"] + +input: + - - sample: + type: map + description: | + Groovy Map containing sample information + e.g. `[ id:'test' ]` + - fitness_estimation_tsv: + type: file + description: TSV file containing calculated fitness estimates and associated statistics + pattern: "*.{tsv}" + +output: + - counts_corr_pdf: + - sample: + type: map + description: Groovy Map containing sample information + - fitness_estimation_count_correlation.pdf: + type: file + description: PDF plot showing the correlation of variant counts between samples/replicates + pattern: "fitness_estimation_count_correlation.pdf" + - fitness_corr_pdf: + - sample: + type: map + description: Groovy Map containing sample information + - fitness_estimation_fitness_correlation.pdf: + type: file + description: PDF plot showing the correlation of fitness scores between samples/replicates + pattern: "fitness_estimation_fitness_correlation.pdf" + - versions: + - versions.yml: + type: file + description: File containing software versions + pattern: "versions.yml" + +authors: + - "@BenjaminWehnert1008" + - "@MaximilianStammnitz" diff --git a/modules/local/fitness/fitness_QC/templates/fitness_QC.R b/modules/local/fitness/fitness_QC/templates/fitness_QC.R new file mode 100644 index 0000000..6df7dfe --- /dev/null +++ b/modules/local/fitness/fitness_QC/templates/fitness_QC.R @@ -0,0 +1,128 @@ +#!/usr/bin/env Rscript + +## fitness QC plots for nf-core/deepmutscan +## 28.10.2025 +## maximilian.stammnitz@crg.eu + +## lower panels: scatter + x=y (log-log version for counts) +panel_xy_abline_counts <- function(x, y, ...) { + op <- par("xpd"); on.exit(par(xpd = op), add = TRUE) + par(xpd = FALSE) + points(x, y, pch = 16, cex = 0.1, ...) + abline(a = 0, b = 1, lty = 2, col = "grey50") +} + +## upper panels: Pearson r (log-log-transformed) +panel_cor_counts <- function(x, y, digits = 2, prefix = "r = ", cex.text = 1.4, ...) { + r <- suppressWarnings(cor(log(x), log(y), use = "pairwise.complete.obs", method = "pearson")) + lab <- if (is.finite(r)) bquote(italic(r) == .(round(r, digits))) else bquote(italic(r) == NA) + + ## Save/restore full graphics state we touch + op <- par(c("usr", "xpd", "xlog", "ylog")) + on.exit(par(op), add = TRUE) + + ## Draw in normalized 0..1 panel coords with logs OFF so text is visible + par(xlog = FALSE, ylog = FALSE, xpd = FALSE, usr = c(0, 1, 0, 1)) + text(0.5, 0.5, labels = lab, cex = cex.text, font = 1, col = "black") +} + +## lower panels: scatter + x=y (linear version for fitness) +panel_xy_abline_fitness <- function(x, y, ...) { + op <- par("xpd"); on.exit(par(xpd = op), add = TRUE) + par(xpd = FALSE) + points(x, y, pch = 16, cex = 0.5, ...) + abline(a = 0, b = 1, lty = 2, col = "grey50") +} + +## upper panels: Pearson r (linear) +panel_cor_fitness <- function(x, y, digits = 2, prefix = "r = ", cex.text = 1.4, ...) { + r <- suppressWarnings(cor(x, y, use = "pairwise.complete.obs", method = "pearson")) + lab <- if (is.finite(r)) bquote(italic(r) == .(round(r, digits))) else bquote(italic(r) == NA) + + ## Save/restore full graphics state we touch + op <- par(c("usr", "xpd", "xlog", "ylog")) + on.exit(par(op), add = TRUE) + + ## Draw in normalized 0..1 panel coords with logs OFF so text is visible + par(xlog = FALSE, ylog = FALSE, xpd = FALSE, usr = c(0, 1, 0, 1)) + text(0.5, 0.5, labels = lab, cex = cex.text, font = 1, col = "black") +} + +#' Plot input/output count correlations and fitness replicate correlations +#' +#' @param fitness_table_path Path to the input table (fitness_estimation.tsv) +#' @param out_counts_corr_pdf Path to write the counts correlation PDF +#' @param out_fitness_corr_pdf Path to write the fitness correlation PDF +#' +#' @return Invisibly returns TRUE; writes the two PDFs. +run_fitness_plots <- function(fitness_table_path, + out_counts_corr_pdf, + out_fitness_corr_pdf) { + + merged.counts.fitness <- read.table(fitness_table_path, sep = "\\t", header = TRUE, check.names = FALSE) + + ## identify the right samples + inputs <- grep("input", colnames(merged.counts.fitness)) + outputs <- grep("output", colnames(merged.counts.fitness)) + + ## 5. Plot input vs. output counts ## + ##################################### + pdf(out_counts_corr_pdf, height = 9, width = 14) + pairs(merged.counts.fitness[, c(inputs, outputs)] + 1, ## use a pseudo-count + lower.panel = panel_xy_abline_counts, + upper.panel = panel_cor_counts, + cex.text = 2, + log = "xy") + dev.off() + + ## 6. Plot fitness correlations ## + ################################## + fitness.repl <- grep("rescaled_fitness", colnames(merged.counts.fitness)) + + if (length(fitness.repl) > 1) { + pdf(out_fitness_corr_pdf, height = 9, width = 14) + pairs(merged.counts.fitness[, fitness.repl], + lower.panel = panel_xy_abline_fitness, + upper.panel = panel_cor_fitness, + cex.text = 2, + xlim = c(-3, 1), + ylim = c(-3, 1)) + dev.off() + } else { + ## If only one (or zero) rescaled_fitness columns exist, still create an empty placeholder + ## so Nextflow finds the declared output. + pdf(out_fitness_corr_pdf, height = 9, width = 14) + plot.new() + title("No replicate fitness columns found (need ≥2 'rescaled_fitness...')\\nCreated placeholder PDF.") + dev.off() + } + + invisible(TRUE) +} + + +##### +# run function +##### +run_fitness_plots( + fitness_table_path = "$fitness_estimation_tsv", + out_counts_corr_pdf = "fitness_estimation_count_correlation.pdf", + out_fitness_corr_pdf = "fitness_estimation_fitness_correlation.pdf" +) + +#### +# create versions.yml +#### +r_version <- strsplit(version[['version.string']], ' ')[[1]][3] + +if (is.null(r_version)) r_version <- "unknown" + +f <- file("versions.yml", "w") +writeLines( + c( + '"${task.process}":', + paste(' r-base:', r_version) + ), + f +) +close(f) diff --git a/modules/local/fitness/fitness_calculation/environment.yml b/modules/local/fitness/fitness_calculation/environment.yml new file mode 100644 index 0000000..197283a --- /dev/null +++ b/modules/local/fitness/fitness_calculation/environment.yml @@ -0,0 +1,15 @@ +channels: + - conda-forge + - bioconda +dependencies: + - bioconda::bioconductor-biostrings=2.78.0 + - conda-forge::r-base=4.5.1 + - conda-forge::r-biocmanager=1.30.27 + - conda-forge::r-dplyr=1.1.4 + - conda-forge::r-ggplot2=4.0.2 + - conda-forge::r-reshape2=1.4.4 + - conda-forge::r-scales=1.4.0 + - conda-forge::r-stringr=1.6.0 + - conda-forge::r-tidyr=1.3.1 + - conda-forge::r-tidyverse=2.0.0 + - conda-forge::r-zoo=1.8_15 diff --git a/modules/local/fitness/fitness_calculation/main.nf b/modules/local/fitness/fitness_calculation/main.nf new file mode 100644 index 0000000..5bf6208 --- /dev/null +++ b/modules/local/fitness/fitness_calculation/main.nf @@ -0,0 +1,29 @@ +process FITNESS_CALCULATION { + tag { sample.sample } + label 'process_single' + + conda "${moduleDir}/environment.yml" + + container "${ workflow.containerEngine == 'singularity' + ? 'https://community-cr-prod.seqera.io/docker/registry/v2/blobs/sha256/73/73a72ec77725aeb67678a74228938fdd6827b669d01a8c96951b1a8ef96eeb0f/data' + : 'community.wave.seqera.io/library/bioconductor-biostrings_bioconductor-mutscan_r-base_r-biocmanager_pruned:c65036d76406f342' }" + + input: + tuple val(sample), path(counts_merged) + path(exp_design) + path(syn_wt_txt) + + output: + tuple val(sample), path("fitness_estimation.tsv"), emit: fitness_estimation + path "versions.yml", emit: versions + + script: + template 'fitness_calculation.R' + + stub: + """ + touch fitness_estimation.tsv + echo "FITNESS_CALCULATION:" > versions.yml + echo " stub-version: 0.0.0" >> versions.yml + """ +} diff --git a/modules/local/fitness/fitness_calculation/meta.yml b/modules/local/fitness/fitness_calculation/meta.yml new file mode 100644 index 0000000..6be779d --- /dev/null +++ b/modules/local/fitness/fitness_calculation/meta.yml @@ -0,0 +1,54 @@ +name: "fitness_calculation" +description: Calculates fitness scores for Deep Mutational Scanning (DMS) variants. It typically uses a log-enrichment ratio approach, comparing variant frequencies in selection (output) versus baseline (input) samples, normalized to a synonymous wild-type reference. +keywords: + - deep mutational scanning + - dms + - fitness calculation + - log-ratio + - enrichment +tools: + - "mutscan": + description: "R package for analysis of deep mutational scanning data" + homepage: "https://bioconductor.org/packages/release/bioc/html/mutscan.html" + documentation: "https://bioconductor.org/packages/release/bioc/manuals/mutscan/man/mutscan.pdf" + tool_dev_url: "https://github.com/csoneson/mutscan" + doi: "10.1186/s12859-023-05187-y" + licence: ["GPL-3.0-or-later"] + +input: + - - sample: + type: map + description: | + Groovy Map containing sample information + e.g. `[ id:'test' ]` + - counts_merged: + type: file + description: TSV or CSV file containing the consolidated variant counts across all replicates + pattern: "*.{tsv,csv}" + - - exp_design: + type: file + description: CSV or text file defining the experimental design, including input/output mappings and replicates + pattern: "*.{csv,txt}" + - - syn_wt_txt: + type: file + description: Text file identifying the synonymous wild-type variant used as the normalization baseline + pattern: "*.txt" + +output: + - fitness_estimation: + - sample: + type: map + description: Groovy Map containing sample information + - fitness_estimation.tsv: + type: file + description: TSV file containing the final calculated fitness scores for all variants + pattern: "fitness_estimation.tsv" + - versions: + - versions.yml: + type: file + description: File containing software versions + pattern: "versions.yml" + +authors: + - "@BenjaminWehnert1008" + - "@MaximilianStammnitz" diff --git a/modules/local/fitness/fitness_calculation/templates/fitness_calculation.R b/modules/local/fitness/fitness_calculation/templates/fitness_calculation.R new file mode 100644 index 0000000..4b0966b --- /dev/null +++ b/modules/local/fitness/fitness_calculation/templates/fitness_calculation.R @@ -0,0 +1,358 @@ +#!/usr/bin/env Rscript + +## default fitness estimation for nf-core/deepmutscan +## 18.02.2026 +## maximilian.stammnitz@crg.eu + +## 0. Libraries ## +################## + +suppressPackageStartupMessages({ + library(Biostrings) +}) + +## --- Helper functions --- + +# calculate nt hamming distances from the specified WT +compute_nt_hamming <- function(merged.counts, wt.seq) { + merged.counts <- cbind("nt_ham" = rep(NA, nrow(merged.counts)), merged.counts) + for (i in 1:nrow(merged.counts)){ + tmp.wt <- strsplit(as.character(wt.seq), "")[[1]] + tmp.mut <- strsplit(as.character(merged.counts\$nt_seq[i]), "")[[1]] + if(length(which(tmp.mut != tmp.wt)) == 0){ + merged.counts\$nt_ham[i] <- 0 + rm(tmp.mut, tmp.wt) + next + }else{ + merged.counts\$nt_ham[i] <- length(which(tmp.mut != tmp.wt)) + rm(tmp.mut, tmp.wt) + next + } + } + merged.counts +} + +# translate sequences and add aa_seq +add_aa_seq <- function(merged.counts) { + merged.counts <- cbind("aa_seq" = as.character(translate(DNAStringSet(merged.counts\$nt_seq))), merged.counts) + merged.counts +} + +# calculate AA hamming distances from the WT +compute_aa_hamming <- function(merged.counts, wt.seq.aa) { + merged.counts <- cbind("aa_ham" = rep(NA, nrow(merged.counts)), merged.counts) + for (i in 1:nrow(merged.counts)){ + tmp.wt <- strsplit(as.character(wt.seq.aa), "")[[1]] + tmp.mut <- strsplit(as.character(merged.counts\$aa_seq[i]), "")[[1]] + if(length(which(tmp.mut != tmp.wt)) == 0){ + merged.counts\$aa_ham[i] <- 0 + rm(tmp.mut, tmp.wt) + next + }else{ + merged.counts\$aa_ham[i] <- length(which(tmp.mut != tmp.wt)) + rm(tmp.mut, tmp.wt) + next + } + } + merged.counts +} + +# name the mutations +name_mutations <- function(merged.counts, wt.seq.aa) { + merged.counts <- cbind("wt_aa" = rep(NA, nrow(merged.counts)), + "pos" = rep(NA, nrow(merged.counts)), + "mut_aa" = rep(NA, nrow(merged.counts)), merged.counts) + for (i in 1:nrow(merged.counts)){ + if(merged.counts\$aa_ham[i] == 0){ + next + }else{ + tmp.wt <- strsplit(as.character(wt.seq.aa), "")[[1]] + tmp.mut <- strsplit(as.character(merged.counts\$aa_seq[i]), "")[[1]] + merged.counts\$pos[i] <- which(tmp.mut != tmp.wt) + merged.counts\$'wt_aa'[i] <- tmp.wt[merged.counts\$pos[i]] + merged.counts\$'mut_aa'[i] <- tmp.mut[merged.counts\$pos[i]] + rm(tmp.mut, tmp.wt) + } + } + merged.counts +} + +# find stops, WT and WT; aggregate counts of variants which are identical on the aa (but not nt) level +aggregate_by_aa <- function(merged.counts) { + ## find stops, WT and WT + merged.counts <- cbind(merged.counts, + "wt" = rep(NA, nrow(merged.counts)), + "stop" = rep(NA, nrow(merged.counts))) + merged.counts\$wt[which(merged.counts\$nt_ham == 0)] <- TRUE + merged.counts\$stop[which(merged.counts\$'mut_aa' == "*")] <- TRUE + + ## aggregate counts of variants which are identical on the aa (but not nt) level + ## exception: wildtype ones + ## thereby shrinking the matrix + uniq.aa.vars <- unique(merged.counts\$aa_seq) + uniq.aa.vars <- uniq.aa.vars[-which(uniq.aa.vars == merged.counts\$aa_seq[which(merged.counts\$wt == TRUE)])] + for(i in 1:length(uniq.aa.vars)){ + tmp.aa_seq <- uniq.aa.vars[i] + hits <- which(as.character(merged.counts\$aa_seq) == tmp.aa_seq) + if(length(hits) == 1){ + rm(tmp.aa_seq, hits) + next + }else{ + for(j in grep("input|output", colnames(merged.counts))){ + merged.counts[hits[1],j] <- sum(merged.counts[hits,j], na.rm = TRUE) + } + merged.counts[hits[1], "nt_seq"] <- paste(merged.counts[hits, "nt_seq"], collapse = ", ") + merged.counts <- merged.counts[-hits[-1],] + rm(tmp.aa_seq, hits) + next + } + } + merged.counts +} + +# calculate bimodal fitness distribution +density_peaks <- function(x, adjust = 1, ...) { + + ## obtain density distribution + d <- density(x, adjust = adjust, n = 5000, na.rm = T, ...) + y <- d\$y + idx <- which(diff(sign(diff(y))) == -2) + 1 + if (length(idx) == 0) return(numeric(0)) + + ## order density peaks by height (y value); take top 2 + idx <- idx[order(y[idx], decreasing = TRUE)] + peaks_x <- d\$x[idx] + peaks_x[seq_len(min(2, length(peaks_x)))] + +} + +# 3. Raw fitness calculations ## +calc_raw_fitness <- function(merged.counts, exp.design) { + ## how many fitness replicates are there + reps <- length(unique(exp.design\$experiment_replicate)) + for (i in 1:reps){ + merged.counts <- cbind(merged.counts, rep(NA, nrow(merged.counts))) + colnames(merged.counts)[ncol(merged.counts)] <- paste0("raw_fitness_rep", i) + } + + ## calculate raw fitness of all variants vs. WT variant + for (i in 1:reps){ + + ### collect counts + tmp.input.counts <- merged.counts[,paste0("input", i)] + tmp.output.counts <- merged.counts[,paste0("output", i)] + + ### add pseudo-count to zero-outputs (if the corresponding input count is non-zero) + tmp.output.counts[which(tmp.output.counts == 0 & tmp.input.counts != 0)] <- 1 + + ### take logs + tmp.wt.log.ratio <- log(tmp.output.counts[which(merged.counts\$wt == TRUE)] / + tmp.input.counts[which(merged.counts\$wt == TRUE)]) + tmp.fitness <- log(tmp.output.counts / + tmp.input.counts) - tmp.wt.log.ratio + + ### uncertain values to NA + tmp.fitness[which(is.na(tmp.fitness) == TRUE)] <- NA + tmp.fitness[which(tmp.fitness == "Inf")] <- NA + + ### add to table + merged.counts[,c(ncol(merged.counts) - reps + i)] <- tmp.fitness + + ### clean up + rm(tmp.fitness, tmp.wt.log.ratio, tmp.output.counts, tmp.input.counts) + } + + list(merged.counts = merged.counts, reps = reps) +} + +# 4. Fitness and error refinements ## +rescale_and_summarize <- function(merged.counts, reps) { + ## center the raw fitness distributions on 0 (median of wildtype synonymous) and -1 (median of stops) + for (i in 1:reps){ + + merged.counts <- cbind(merged.counts, rep(NA, nrow(merged.counts))) + colnames(merged.counts)[ncol(merged.counts)] <- paste0("rescaled_fitness_rep", i) + + ### anchors: median fitness of synonymous (aa_ham == 0) -> 0, of stops -> -1 + raw_col <- ncol(merged.counts) - reps + tmp.raw <- merged.counts[,raw_col] + tmp.wt.fitness.med <- median(tmp.raw[which(merged.counts\$aa_ham == 0)], na.rm = TRUE) + tmp.stop.fitness.med <- median(tmp.raw[which(merged.counts\$stop == TRUE)], na.rm = TRUE) + + ### if an anchor is missing, or the two anchors coincide (e.g. on sparse data, + ### where lm() would return an NA slope), fall back to the modes of the + ### (bimodal) fitness distribution to define the missing/degenerate anchor + if(is.na(tmp.wt.fitness.med) | is.na(tmp.stop.fitness.med) | + (!is.na(tmp.wt.fitness.med) & !is.na(tmp.stop.fitness.med) & tmp.wt.fitness.med == tmp.stop.fitness.med)){ + tmp.peaks <- sort(density_peaks(x = tmp.raw)) + tmp.hi <- if(!is.na(tmp.wt.fitness.med)) tmp.wt.fitness.med else if(length(tmp.peaks) >= 1) tmp.peaks[length(tmp.peaks)] else NA + if(!is.na(tmp.stop.fitness.med) & (is.na(tmp.hi) || tmp.stop.fitness.med != tmp.hi)){ + tmp.lo <- tmp.stop.fitness.med + }else{ + tmp.cand <- tmp.peaks[!is.na(tmp.peaks) & tmp.peaks != tmp.hi] + tmp.lo <- if(length(tmp.cand) >= 1) tmp.cand[1] else NA + } + tmp.wt.fitness.med <- tmp.hi + tmp.stop.fitness.med <- tmp.lo + rm(tmp.peaks, tmp.hi) + } + + ### two distinct anchors -> linear rescale; a single usable anchor -> WT-centre only + if(!is.na(tmp.wt.fitness.med) & !is.na(tmp.stop.fitness.med) & tmp.wt.fitness.med != tmp.stop.fitness.med){ + lm.rescale <- lm(c(0, -1) ~ c(tmp.wt.fitness.med, tmp.stop.fitness.med)) + merged.counts[,ncol(merged.counts)] <- tmp.raw * lm.rescale\$coefficients[[2]] + lm.rescale\$coefficients[[1]] + rm(lm.rescale) + }else if(!is.na(tmp.wt.fitness.med)){ + merged.counts[,ncol(merged.counts)] <- tmp.raw - tmp.wt.fitness.med + } + + rm(raw_col, tmp.raw, tmp.wt.fitness.med, tmp.stop.fitness.med) + } + + ## calculate fitness mean and standard deviation across replicates + merged.counts <- cbind(merged.counts, + "mean fitness" = rep(NA, nrow(merged.counts)), + "fitness sd" = rep(NA, nrow(merged.counts))) + + if(reps == 1){ + + merged.counts\$'mean fitness' <- merged.counts[,ncol(merged.counts) - 2] + + }else if(reps > 1){ + + merged.counts\$'mean fitness' <- apply(merged.counts[,c(ncol(merged.counts) - 1 - reps):c(ncol(merged.counts) - 2)], + 1, + mean, + na.rm = TRUE) + merged.counts\$'fitness sd' <- apply(merged.counts[,c(ncol(merged.counts) - 1 - reps):c(ncol(merged.counts) - 2)], + 1, + sd, + na.rm = TRUE) + + } + + merged.counts +} + +## --- Main function --- + +#' Run default fitness estimation with configurable I/O paths +#' +#' @param counts_path Path to counts_merged.tsv +#' @param design_path Path to experimentalDesign.tsv +#' @param wt_seq_path Path to synonymous_wt.txt (single line DNA sequence) +#' @param output_path Path to write fitness_estimation.tsv +#' +#' @return Invisibly returns the final data.frame; writes the output to output_path. +run_fitness_estimation <- function(counts_path, + design_path, + wt_seq_path, + output_path) { + ## 1. Import key files ## + ######################### + + merged.counts <- read.table(counts_path, sep = "\t", header = TRUE, check.names = FALSE) + exp.design <- read.table(design_path, sep = "\t", header = TRUE, check.names = FALSE) + wt.seq <- DNAString(as.character(read.table(wt_seq_path))) + wt.seq.aa <- translate(wt.seq) + + ## 2. Pre-processing the count table ## + ####################################### + + ## calculate nt hamming distances from the specified WT + merged.counts <- compute_nt_hamming(merged.counts, wt.seq) + + ## translate sequences + merged.counts <- add_aa_seq(merged.counts) + + ## calculate AA hamming distances from the WT + merged.counts <- compute_aa_hamming(merged.counts, wt.seq.aa) + + ## name the mutations + merged.counts <- name_mutations(merged.counts, wt.seq.aa) + + ## find stops, WT and WT; aggregate AA-identical variants (except WT) + merged.counts <- aggregate_by_aa(merged.counts) + + ## 3. Raw fitness calculations ## + ################################# + fitness_res <- calc_raw_fitness(merged.counts, exp.design) + merged.counts <- fitness_res\$merged.counts + reps <- fitness_res\$reps + + ## 4. Fitness and error refinements ## + ###################################### + merged.counts <- rescale_and_summarize(merged.counts, reps) + + ## clean up + rm(reps) + + ## export + write.table(merged.counts, output_path, + col.names = TRUE, row.names = FALSE, quote = FALSE, sep = "\t", na = "") + + invisible(merged.counts) +} + + +## 5. Version ## +################ + +# sessionInfo() +# R version 4.5.1 (2025-06-13) +# Platform: aarch64-apple-darwin20 +# Running under: macOS Sonoma 14.6.1 +# +# Matrix products: default +# BLAS: /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libBLAS.dylib +# LAPACK: /Library/Frameworks/R.framework/Versions/4.5-arm64/Resources/lib/libRlapack.dylib; LAPACK version 3.12.1 +# +# locale: +# [1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8 +# +# time zone: Europe/Madrid +# tzcode source: internal +# +# attached base packages: +# [1] stats4 stats graphics grDevices utils datasets methods base +# +# other attached packages: +# [1] Biostrings_2.76.0 GenomeInfoDb_1.44.2 XVector_0.48.0 IRanges_2.42.0 S4Vectors_0.46.0 +# [6] BiocGenerics_0.54.0 generics_0.1.4 +# +# loaded via a namespace (and not attached): +# [1] httr_1.4.7 compiler_4.5.1 R6_2.6.1 tools_4.5.1 +# [5] GenomeInfoDbData_1.2.14 rstudioapi_0.17.1 crayon_1.5.3 UCSC.utils_1.4.0 +# [9] jsonlite_2.0.0 + + + +##### +# run function +##### +run_fitness_estimation( + counts_path = "$counts_merged", + design_path = "$exp_design", + wt_seq_path = "$syn_wt_txt", + output_path = "fitness_estimation.tsv" +) + +#### +# create versions.yml +#### +r_version <- strsplit(version[['version.string']], ' ')[[1]][3] +Biostrings_version <- as.character(packageVersion("Biostrings")) + +if (is.null(r_version)) r_version <- "unknown" +if (length(Biostrings_version) == 0) Biostrings_version <- "unknown" + +f <- file("versions.yml", "w") +writeLines( + c( + '"${task.process}":', + paste(' r-base:', r_version), + paste(' r-Biostrings:', Biostrings_version) + ), + f +) +close(f) diff --git a/modules/local/fitness/fitness_experimental_design/environment.yml b/modules/local/fitness/fitness_experimental_design/environment.yml new file mode 100644 index 0000000..197283a --- /dev/null +++ b/modules/local/fitness/fitness_experimental_design/environment.yml @@ -0,0 +1,15 @@ +channels: + - conda-forge + - bioconda +dependencies: + - bioconda::bioconductor-biostrings=2.78.0 + - conda-forge::r-base=4.5.1 + - conda-forge::r-biocmanager=1.30.27 + - conda-forge::r-dplyr=1.1.4 + - conda-forge::r-ggplot2=4.0.2 + - conda-forge::r-reshape2=1.4.4 + - conda-forge::r-scales=1.4.0 + - conda-forge::r-stringr=1.6.0 + - conda-forge::r-tidyr=1.3.1 + - conda-forge::r-tidyverse=2.0.0 + - conda-forge::r-zoo=1.8_15 diff --git a/modules/local/fitness/fitness_experimental_design/main.nf b/modules/local/fitness/fitness_experimental_design/main.nf new file mode 100644 index 0000000..b1793ba --- /dev/null +++ b/modules/local/fitness/fitness_experimental_design/main.nf @@ -0,0 +1,29 @@ +process EXPDESIGN_FITNESS { + tag "experimentalDesign" + label 'process_single' + + conda "${moduleDir}/environment.yml" + + container "${ workflow.containerEngine == 'singularity' + ? 'https://community-cr-prod.seqera.io/docker/registry/v2/blobs/sha256/73/73a72ec77725aeb67678a74228938fdd6827b669d01a8c96951b1a8ef96eeb0f/data' + : 'community.wave.seqera.io/library/bioconductor-biostrings_bioconductor-mutscan_r-base_r-biocmanager_pruned:c65036d76406f342' }" + + input: + path samplesheet_csv + + output: + path "experimentalDesign.tsv", emit: experimental_design + path "versions.yml", emit: versions + + script: + template 'dimsum_experimentalDesign.R' + + stub: + """ + touch experimentalDesign.tsv + cat <<-END_VERSIONS > versions.yml + "${task.process}": + r-base: "0.0.0" + END_VERSIONS + """ +} diff --git a/modules/local/fitness/fitness_experimental_design/meta.yml b/modules/local/fitness/fitness_experimental_design/meta.yml new file mode 100644 index 0000000..74d5c93 --- /dev/null +++ b/modules/local/fitness/fitness_experimental_design/meta.yml @@ -0,0 +1,37 @@ +name: "expdesign_fitness" +description: Transforms a standard nf-core samplesheet into a tab-separated experimental design file that defines replicates, selection conditions, and input/output relationships for fitness calculation. +keywords: + - deep mutational scanning + - dms + - experimental design + - samplesheet + - dimsum + - mutscan +tools: + - "r-base": + description: "R is a free software environment for statistical computing and graphics" + homepage: "https://www.r-project.org/" + documentation: "https://cran.r-project.org/manuals.html" + licence: ["GPL-2.0-or-later"] + +input: + - - samplesheet_csv: + type: file + description: The primary nf-core pipeline samplesheet containing metadata for all sequencing libraries + pattern: "*.{csv}" + +output: + - experimental_design: + - experimentalDesign.tsv: + type: file + description: A TSV file formatted specifically for DiMSum or mutscan experimental design requirements + pattern: "experimentalDesign.tsv" + - versions: + - versions.yml: + type: file + description: File containing software versions + pattern: "versions.yml" + +authors: + - "@BenjaminWehnert1008" + - "@MaximilianStammnitz" diff --git a/modules/local/fitness/fitness_experimental_design/templates/dimsum_experimentalDesign.R b/modules/local/fitness/fitness_experimental_design/templates/dimsum_experimentalDesign.R new file mode 100644 index 0000000..475353d --- /dev/null +++ b/modules/local/fitness/fitness_experimental_design/templates/dimsum_experimentalDesign.R @@ -0,0 +1,111 @@ +#!/usr/bin/env Rscript + +# Make a DiMSum experimental design from a deepmutscan samplesheet. +# - samplesheet_csv: path to CSV with columns sample,type,replicate,file1,file2 +# - out_path: where to write the TSV (default "experimentalDesign.tsv") +# Returns: the experimental design as a data.frame +make_dimsum_experimental_design <- function(samplesheet_csv, out_path = "experimentalDesign.tsv") { + # ---- read & normalize ---- + ss <- read.csv(samplesheet_csv, stringsAsFactors = FALSE, check.names = FALSE) + names(ss) <- tolower(names(ss)) + + # tolerate missing file2 column (single-end) + if (!"file2" %in% names(ss)) ss\$file2 <- "" + + required <- c("sample", "type", "replicate", "file1", "file2") + missing <- setdiff(required, names(ss)) + if (length(missing) > 0) stop("Samplesheet missing columns: ", paste(missing, collapse = ", ")) + + # coerce types + ss\$replicate <- as.integer(ss\$replicate) + + # ---- keep only the samples DiMSum scores ---- + # A samplesheet legitimately carries other row types: `wildtype` (required by + # --error_correction wildtype) and `quality`. They are real samples, but they are not part of the + # selection design - they feed error correction and QC - and MERGE_COUNTS drops them too. Leaving + # them in gave them an empty `selection_id`, which DiMSum reads back as NA and then dies in + # dimsum__check_experiment_design on `sum(exp_design[,"selection_id"] < 0)` with the opaque + # "missing value where TRUE/FALSE needed". + ss <- ss[ss\$type %in% c("input", "output"), , drop = FALSE] + if (nrow(ss) == 0) { + stop("Samplesheet has no 'input'/'output' rows - DiMSum needs both to compute fitness.") + } + + # ---- order rows: by sample (if multiple), input before output, then replicate ---- + # Sorting happens before naming, because the names are derived from the row order below. + multi_base <- length(unique(ss\$sample)) > 1 + type_rank <- match(ss\$type, c("input", "output")) + ord <- if (multi_base) { + order(ss\$sample, type_rank, ss\$replicate) + } else { + order(type_rank, ss\$replicate) + } + ss <- ss[ord, , drop = FALSE] + + # ---- sample_name: must equal the count column MERGE_COUNTS writes for this row ---- + # merge_counts.R names its columns positionally - paste0("input", seq_len(n_in)) over the files + # sorted by replicate - so the name has to come from each row's POSITION within its type, not from + # the samplesheet's `replicate` value. The two only coincide when the replicates happen to be a + # contiguous 1..N; replicates 1,2,4 would otherwise produce "input4" for a column named "input3" + # and DiMSum could not match them. DiMSum also requires sample_name to be alphanumeric and to + # start with a letter, so the parts are joined without a separator. + idx <- stats::ave(seq_len(nrow(ss)), ss\$sample, ss\$type, FUN = seq_along) + # NOTE: for multiple biological samples these names still will not match the counts file, because + # one design is built from the whole samplesheet and broadcast to every per-sample DiMSum run + # (see subworkflows/local/calculate_fitness/main.nf). Multi-sample + DiMSum is a separate, + # known-broken case; single-sample runs are correct. + sample_name <- if (multi_base) paste0(ss\$sample, ss\$type, idx) else paste0(ss\$type, idx) + + # ---- build DiMSum columns ---- + experiment_replicate <- ss\$replicate # pairs an input with an output; need not be 1..N + selection_id <- ifelse(ss\$type == "input", 0L, 1L) + # assume one selection batch; DiMSum requires this to be blank for input samples + selection_replicate <- ifelse(ss\$type == "output", 1L, NA_integer_) + # assume one technical batch + technical_replicate <- rep(1L, nrow(ss)) + + pair1 <- basename(ss\$file1) + # keep empty string for single-end / missing file2 + pair2 <- ifelse(is.na(ss\$file2) | ss\$file2 == "", "", basename(ss\$file2)) + + ed <- data.frame( + sample_name = sample_name, + experiment_replicate = experiment_replicate, + selection_id = selection_id, + selection_replicate = selection_replicate, + technical_replicate = technical_replicate, + pair1 = pair1, + pair2 = pair2, + stringsAsFactors = FALSE + ) + rownames(ed) <- NULL + + # ---- write & return ---- + write.table(ed, file = out_path, sep = "\\t", row.names = FALSE, col.names = TRUE, quote = FALSE, na = "") + return(ed) +} + +##### +# run function +##### +make_dimsum_experimental_design( + samplesheet_csv = "$samplesheet_csv", + out_path = "experimentalDesign.tsv" +) + +#### +# create versions.yml +#### +r_version <- strsplit(version[['version.string']], ' ')[[1]][3] + +if (is.null(r_version)) r_version <- "unknown" + +f <- file("versions.yml", "w") +writeLines( + c( + '"${task.process}":', + paste(' r-base:', r_version) + ), + f +) +close(f) diff --git a/modules/local/fitness/fitness_heatmap/environment.yml b/modules/local/fitness/fitness_heatmap/environment.yml new file mode 100644 index 0000000..197283a --- /dev/null +++ b/modules/local/fitness/fitness_heatmap/environment.yml @@ -0,0 +1,15 @@ +channels: + - conda-forge + - bioconda +dependencies: + - bioconda::bioconductor-biostrings=2.78.0 + - conda-forge::r-base=4.5.1 + - conda-forge::r-biocmanager=1.30.27 + - conda-forge::r-dplyr=1.1.4 + - conda-forge::r-ggplot2=4.0.2 + - conda-forge::r-reshape2=1.4.4 + - conda-forge::r-scales=1.4.0 + - conda-forge::r-stringr=1.6.0 + - conda-forge::r-tidyr=1.3.1 + - conda-forge::r-tidyverse=2.0.0 + - conda-forge::r-zoo=1.8_15 diff --git a/modules/local/fitness/fitness_heatmap/main.nf b/modules/local/fitness/fitness_heatmap/main.nf new file mode 100644 index 0000000..4106ba6 --- /dev/null +++ b/modules/local/fitness/fitness_heatmap/main.nf @@ -0,0 +1,30 @@ +process FITNESS_HEATMAP { + tag { sample.sample } + label 'process_single' + + conda "${moduleDir}/environment.yml" + + container "${ workflow.containerEngine == 'singularity' + ? 'https://community-cr-prod.seqera.io/docker/registry/v2/blobs/sha256/73/73a72ec77725aeb67678a74228938fdd6827b669d01a8c96951b1a8ef96eeb0f/data' + : 'community.wave.seqera.io/library/bioconductor-biostrings_bioconductor-mutscan_r-base_r-biocmanager_pruned:c65036d76406f342' }" + + input: + tuple val(sample), path(fitness_estimation_tsv) // from FITNESS_CALCULATION + tuple val(sample2), path(wt_seq) // WT sequence + + output: + tuple val(sample), path("fitness_heatmap.pdf"), emit: fitness_heatmap + path "versions.yml", emit: versions + + script: + template 'fitness_heatmap.R' + + stub: + """ + touch fitness_heatmap.pdf + cat > versions.yml <<'EOF' + FITNESS_HEATMAP: + stub-version: "0.0.0" + EOF + """ +} diff --git a/modules/local/fitness/fitness_heatmap/meta.yml b/modules/local/fitness/fitness_heatmap/meta.yml new file mode 100644 index 0000000..7b22869 --- /dev/null +++ b/modules/local/fitness/fitness_heatmap/meta.yml @@ -0,0 +1,59 @@ +name: "fitness_heatmap" +description: Generates a fitness landscape heatmap from calculated fitness scores, displaying the functional impact of amino acid substitutions at each position of the reference sequence. +keywords: + - deep mutational scanning + - dms + - fitness + - visualization + - heatmap + - mutation landscape +tools: + - "mutscan": + description: "R package for analysis of deep mutational scanning data" + homepage: "https://bioconductor.org/packages/release/bioc/html/mutscan.html" + documentation: "https://bioconductor.org/packages/release/bioc/manuals/mutscan/man/mutscan.pdf" + tool_dev_url: "https://github.com/csoneson/mutscan" + doi: "10.1186/s12859-023-05187-y" + licence: ["GPL-3.0-or-later"] + - "bioconductor-biostrings": + description: "Efficient manipulation of biological strings in R" + homepage: "https://bioconductor.org/packages/Biostrings" + licence: ["Artistic-2.0"] + +input: + - - sample: + type: map + description: | + Groovy Map containing sample information for the fitness data + e.g. `[ id:'test' ]` + - fitness_estimation_tsv: + type: file + description: TSV file containing calculated fitness scores and statistics + pattern: "*.{tsv}" + - - sample2: + type: map + description: | + Groovy Map containing metadata for the reference sequence + - wt_seq: + type: file + description: FASTA file or text file containing the wild-type reference sequence + pattern: "*.{fasta,fa,txt}" + +output: + - fitness_heatmap: + - sample: + type: map + description: Groovy Map containing sample information + - fitness_heatmap.pdf: + type: file + description: PDF heatmap visualizing the fitness scores across the protein sequence + pattern: "fitness_heatmap.pdf" + - versions: + - versions.yml: + type: file + description: File containing software versions + pattern: "versions.yml" + +authors: + - "@BenjaminWehnert1008" + - "@MaximilianStammnitz" diff --git a/modules/local/fitness/fitness_heatmap/templates/fitness_heatmap.R b/modules/local/fitness/fitness_heatmap/templates/fitness_heatmap.R new file mode 100644 index 0000000..e860382 --- /dev/null +++ b/modules/local/fitness/fitness_heatmap/templates/fitness_heatmap.R @@ -0,0 +1,312 @@ +#!/usr/bin/env Rscript + +suppressPackageStartupMessages({ + library(methods) + library(dplyr) + library(ggplot2) + library(grid) # for unit() +}) + +# ---------- helper functions ---------- +find_col <- function(df, candidates) { + norm <- function(x) gsub("[^a-z0-9]+", "_", tolower(x)) + nms <- colnames(df); nn <- norm(nms) + for (cand in candidates) { + hit <- which(nn == norm(cand)) + if (length(hit) == 1) return(nms[hit]) + } + stop(sprintf("Could not find any of columns: %s", paste(candidates, collapse = ", "))) +} + +get_rescaled_cols <- function(df) { + nms <- colnames(df) + hits <- grep("^rescaled[_ ]?fitness", nms, ignore.case = TRUE, value = TRUE) + if (!length(hits)) stop("No 'rescaled_fitness' columns found.") + idx <- suppressWarnings(as.integer(gsub(".*?([0-9]+)\$", "\\\\1", hits))) # additional backslashs to make it groovy readable + hits[order(is.na(idx), idx, hits)] +} + +# Find "mean fitness" column (if present) +find_mean_col <- function(df) { + nms <- colnames(df) + key <- tolower(gsub("[^a-z0-9]+", "_", nms)) + hit <- which(key == "mean_fitness") + if (length(hit) == 1) nms[hit] else NULL +} + +# NEW: read WT amino acid sequence from .txt file (single line) +read_wt_seq_aa_txt <- function(path) { + if (is.null(path)) stop("wt_seq_aa_txt_path must be provided.") + x <- readLines(path, warn = FALSE) + x <- x[nzchar(x)] + if (!length(x)) stop("WT AA TXT is empty.") + aa <- toupper(gsub("\\\\s+", "", x[which.max(nchar(x))])) + # Keep only valid amino acid letters (including stop '*') + aa <- gsub("[^ACDEFGHIKLMNPQRSTVWY*]", "", aa) + if (!nchar(aa)) stop("WT AA TXT contains no valid AA letters.") + aa +} + +# Build full AA×position grid for positions 1..wt_len (from WT sequence), +# join fitness values; any missing combos stay NA (-> grey). +# Positions > wt_len are considered padded and will be white. +build_heatmap_long <- function(df, + wt_aa_col, + pos_col, + mut_aa_col, + fitness_col, + positions_per_row = 75, + wt_seq_aa, + fill_missing_as_zero = FALSE) { + + # authoritative WT length from provided sequence + letters <- strsplit(wt_seq_aa, "", fixed = TRUE)[[1]] + wt_len <- length(letters) + + # normalize data + df0 <- df %>% + transmute( + position = suppressWarnings(as.numeric(.data[[pos_col]])), + wt_aa_in = .data[[wt_aa_col]], + mut_aa = .data[[mut_aa_col]], + fitness = suppressWarnings(as.numeric(.data[[fitness_col]])) + ) %>% + filter(is.finite(position)) + + # drop any rows that claim positions beyond WT length + if (nrow(df0) && any(df0\$position > wt_len, na.rm = TRUE)) { + dropped <- sum(df0\$position > wt_len, na.rm = TRUE) + warning(sprintf("Dropping %d row(s) with position > WT length (%d).", dropped, wt_len)) + df0 <- df0 %>% filter(position <= wt_len) + } + + # pad to next multiple of 75 (by rows) + rem <- wt_len %% positions_per_row + pad_need <- if (rem == 0) 0 else positions_per_row - rem + max_paded <- wt_len + pad_need + + # full grid: positions 1..max_paded (so the tail exists), AA set of 21 + all_positions <- seq_len(max_paded) + all_amino_acids <- c("A","C","D","E","F","G","H","I","K","L", + "M","N","P","Q","R","S","T","V","W","Y","*") + + grid_df <- expand.grid(position = all_positions, + mut_aa = all_amino_acids, + KEEP.OUT.ATTRS = FALSE, stringsAsFactors = FALSE) %>% + mutate(is_padded = position > wt_len) + + # join fitness only for real positions (<= wt_len) + fit_df <- df0 %>% select(position, mut_aa, fitness) + d <- grid_df %>% + left_join(fit_df, by = c("position","mut_aa")) + + if (fill_missing_as_zero) { + d\$fitness[is.na(d\$fitness) & d\$position <= wt_len] <- 0 + } + + # authoritative WT AA per real position; tail gets placeholder 'Y' + wt_map <- tibble(position = seq_len(wt_len), wt_aa = letters) + d <- d %>% + left_join(wt_map, by = "position") %>% + mutate(wt_aa = ifelse(is.na(wt_aa) & position > wt_len, "Y", wt_aa)) + + # layout fields + d <- d %>% + mutate( + row_group = ((position - 1) %/% positions_per_row) + 1, + wt_aa_pos = paste0(wt_aa, position), + wt_aa_pos = factor(wt_aa_pos, levels = unique(wt_aa_pos)), + synonymous = mut_aa == wt_aa + ) + + # IMPORTANT: use WT length as the true end of the protein + d\$max_pos <- wt_len + d +} + +syn_segments <- function(d, positions_per_row = 75) { + amino_order <- rev(c("G", "A", "V", "L", "M", "I", "F", + "Y", "W", "K", "R", "H", "D", "E", + "S", "T", "C", "N", "Q", "P", "*")) + d %>% + mutate( + mut_aa = factor(mut_aa, levels = amino_order), + x = as.numeric(factor(wt_aa_pos, levels = levels(wt_aa_pos))) - + ((row_group - 1) * positions_per_row), + y = as.numeric(factor(mut_aa, levels = amino_order)) + ) %>% + filter(synonymous, position <= max_pos) +} + +# Draw one solid white rectangle per row group covering the padded tail region +white_tail_rects <- function(d, positions_per_row = 75) { + wt_len <- unique(d\$max_pos)[1] + if (!is.finite(wt_len)) return(dplyr::tibble()[0,]) + + # if perfectly divisible by 75, there is no tail to cover + if (wt_len %% positions_per_row == 0) return(dplyr::tibble()[0,]) + + # which facet (row group) contains the last real position? + last_group <- ((wt_len - 1) %/% positions_per_row) + 1 + last_local_idx <- ((wt_len - 1) %% positions_per_row) + 1 # 1..75 within the facet + + tibble::tibble( + row_group = last_group, + xmin = last_local_idx + 0.5 - 0.025, # tiny epsilon to avoid hairlines + xmax = positions_per_row + 0.5 + 0.025, + ymin = 0.5 - 0.025, + ymax = 21.5 + 0.025 + ) +} + +plot_heatmap <- function(d, title_text, positions_per_row = 75) { + amino_order <- rev(c("G", "A", "V", "L", "M", "I", "F", + "Y", "W", "K", "R", "H", "D", "E", + "S", "T", "C", "N", "Q", "P", "*")) + d <- d %>% mutate(mut_aa = factor(mut_aa, levels = amino_order)) + + min_f <- suppressWarnings(min(d\$fitness, na.rm = TRUE)); if (!is.finite(min_f)) min_f <- 0 + max_f <- suppressWarnings(max(d\$fitness, na.rm = TRUE)); if (!is.finite(max_f)) max_f <- 0 + max_orig_pos <- unique(d\$max_pos)[1] + + syn <- syn_segments(d, positions_per_row) + rect <- white_tail_rects(d, positions_per_row) + + ggplot(d, aes(x = wt_aa_pos, y = mut_aa, fill = fitness)) + + scale_fill_gradientn( + colours = c("#D73027", "#F0F0F0", "#4575B4"), + values = if ((abs(min_f) + max_f) > 0) c(0, abs(min_f)/(abs(min_f)+max_f), 1) else c(0, 0.5, 1), + na.value = "grey35", + limits = c(min_f, max_f) + ) + + scale_x_discrete( + labels = function(x) { + num <- suppressWarnings(as.numeric(gsub("[^0-9]", "", x))) + ifelse(num > max_orig_pos, " ", x) + }, + expand = expansion(mult = c(0, 0)) # no extra margin area + ) + + geom_tile() + + # Solid white block covering the tail (no pattern / no seams) + { if (nrow(rect)) geom_rect(data = rect, inherit.aes = FALSE, + aes(xmin = xmin, xmax = xmax, ymin = ymin, ymax = ymax), + fill = "white", color = NA) } + + geom_segment( + data = syn, + aes(x = x - 0.485, xend = x + 0.485, y = y - 0.485, yend = y + 0.485), + linewidth = 0.2, inherit.aes = FALSE, color = "grey10" + ) + + theme_minimal() + + labs(title = title_text, x = "Wild-type amino acid", y = "Mutant amino acid", fill = "Fitness") + + theme( + plot.title = element_text(size = 16, face = "bold"), + axis.text.x = element_text(angle = 90, hjust = 1, vjust = 0.5, size = 10), + axis.text.y = element_text(size = 10), + axis.title.x = element_text(size = 14), + axis.title.y = element_text(size = 14), + legend.title = element_text(size = 12), + legend.text = element_text(size = 10), + panel.grid.major = element_blank(), + panel.grid.minor = element_blank(), + strip.text = element_blank(), + strip.background = element_blank(), + panel.spacing = grid::unit(0.2, "lines") + ) + + facet_wrap(~ row_group, scales = "free_x", ncol = 1) +} + +# ---------- main callable ---------- +# fitness_table_path : path to fitness_estimation.tsv +# wt_seq_aa_txt_path : path to TXT file containing WT AA sequence (one line) +# output_pdf_path : output PDF (default "fitness_heatmap.pdf") +# positions_per_row : default 75 +run_fitness_rescaled_heatmaps <- function(fitness_table_path, + wt_seq_aa_txt_path, + output_pdf_path = "fitness_heatmap.pdf", + positions_per_row = 75) { + + df <- read.table( + fitness_table_path, sep = "\t", header = TRUE, + check.names = FALSE, quote = "", comment.char = "" + ) + + wt_aa_col <- find_col(df, c("wt aa", "wt_aa", "wt")) + pos_col <- find_col(df, c("pos", "position")) + mut_aa_col <- find_col(df, c("mut aa", "mut_aa", "aa")) + rescaled_cols <- get_rescaled_cols(df) + + wt_seq_aa <- read_wt_seq_aa_txt(wt_seq_aa_txt_path) + + plots <- list() + + ## 1) Mean first – use existing "mean fitness" column if available + mean_col <- find_mean_col(df) + if (is.null(mean_col)) { + df\$`rescaled_fitness_mean` <- if (length(rescaled_cols) == 1) df[[rescaled_cols[1]]] else rowMeans(df[, rescaled_cols], na.rm = TRUE) + mean_col <- "rescaled_fitness_mean" + } + long_df_mean <- build_heatmap_long(df, wt_aa_col, pos_col, mut_aa_col, mean_col, + positions_per_row, wt_seq_aa) + plots[[length(plots) + 1]] <- list( + title = sprintf("Fitness — mean of %d replicate(s)", length(rescaled_cols)), + data = long_df_mean + ) + + ## 2) Then individual replicates + for (i in seq_along(rescaled_cols)) { + col <- rescaled_cols[i] + long_df <- build_heatmap_long(df, wt_aa_col, pos_col, mut_aa_col, col, + positions_per_row, wt_seq_aa) + plots[[length(plots) + 1]] <- list( + title = sprintf("Fitness — rep%d", i), + data = long_df + ) + } + + # Device height: (#row groups × 4) + page_heights <- vapply(plots, function(p) max(p\$data\$row_group, na.rm = TRUE), numeric(1)) + device_height <- max(4, as.numeric(page_heights) * 4, na.rm = TRUE) + + grDevices::pdf(output_pdf_path, width = 16, height = device_height) + on.exit(try(grDevices::dev.off(), silent = TRUE), add = TRUE) + for (p in plots) print(plot_heatmap(p\$data, p\$title, positions_per_row)) + invisible(TRUE) +} + +#### +# run function +#### +run_fitness_rescaled_heatmaps( + fitness_table_path = "$fitness_estimation_tsv", + wt_seq_aa_txt_path = "$wt_seq", + output_pdf_path = "fitness_heatmap.pdf" +) + +#### +# create versions.yml +#### +r_version <- strsplit(version[['version.string']], ' ')[[1]][3] +dplyr_version <- as.character(packageVersion("dplyr")) +ggplot2_version <- as.character(packageVersion("ggplot2")) +methods_version <- as.character(packageVersion("methods")) +grid_version <- as.character(packageVersion("grid")) + +if (is.null(r_version)) r_version <- "unknown" +if (length(dplyr_version) == 0) dplyr_version <- "unknown" +if (length(ggplot2_version) == 0) ggplot2_version <- "unknown" +if (length(methods_version) == 0) methods_version <- "unknown" +if (length(grid_version) == 0) grid_version <- "unknown" + +f <- file("versions.yml", "w") +writeLines( + c( + '"${task.process}":', + paste(' r-base:', r_version), + paste(' r-dplyr:', dplyr_version), + paste(' r-ggplot2:', ggplot2_version), + paste(' r-methods:', methods_version), + paste(' r-grid:', grid_version) + ), + f +) +close(f) diff --git a/modules/local/fitness/merge_counts/environment.yml b/modules/local/fitness/merge_counts/environment.yml new file mode 100644 index 0000000..197283a --- /dev/null +++ b/modules/local/fitness/merge_counts/environment.yml @@ -0,0 +1,15 @@ +channels: + - conda-forge + - bioconda +dependencies: + - bioconda::bioconductor-biostrings=2.78.0 + - conda-forge::r-base=4.5.1 + - conda-forge::r-biocmanager=1.30.27 + - conda-forge::r-dplyr=1.1.4 + - conda-forge::r-ggplot2=4.0.2 + - conda-forge::r-reshape2=1.4.4 + - conda-forge::r-scales=1.4.0 + - conda-forge::r-stringr=1.6.0 + - conda-forge::r-tidyr=1.3.1 + - conda-forge::r-tidyverse=2.0.0 + - conda-forge::r-zoo=1.8_15 diff --git a/modules/local/fitness/merge_counts/main.nf b/modules/local/fitness/merge_counts/main.nf new file mode 100644 index 0000000..c85d2f4 --- /dev/null +++ b/modules/local/fitness/merge_counts/main.nf @@ -0,0 +1,29 @@ +process MERGE_COUNTS { + tag "${sample.sample}" + label 'process_single' + + conda "${moduleDir}/environment.yml" + + container "${ workflow.containerEngine == 'singularity' + ? 'https://community-cr-prod.seqera.io/docker/registry/v2/blobs/sha256/73/73a72ec77725aeb67678a74228938fdd6827b669d01a8c96951b1a8ef96eeb0f/data' + : 'community.wave.seqera.io/library/bioconductor-biostrings_bioconductor-mutscan_r-base_r-biocmanager_pruned:c65036d76406f342' }" + + input: + tuple val(sample), path(input_counts), path(output_counts) + + output: + tuple val(sample), path("counts_merged.tsv"), emit: merged_counts + path "versions.yml", emit: versions + + script: + template 'merge_counts.R' + + stub: + """ + touch counts_merged.tsv + cat <<-END_VERSIONS > versions.yml + "${task.process}": + r-base: "0.0.0" + END_VERSIONS + """ +} diff --git a/modules/local/fitness/merge_counts/meta.yml b/modules/local/fitness/merge_counts/meta.yml new file mode 100644 index 0000000..87e035a --- /dev/null +++ b/modules/local/fitness/merge_counts/meta.yml @@ -0,0 +1,51 @@ +name: "merge_counts" +description: Consolidates variant counts from multiple input (baseline) and output (selection) files into a single merged TSV table, organized by sample or replicate. +keywords: + - deep mutational scanning + - dms + - count merging + - variant table + - preprocessing +tools: + - "mutscan": + description: "R package for analysis of deep mutational scanning data" + homepage: "https://bioconductor.org/packages/release/bioc/html/mutscan.html" + documentation: "https://bioconductor.org/packages/release/bioc/manuals/mutscan/man/mutscan.pdf" + tool_dev_url: "https://github.com/csoneson/mutscan" + doi: "10.1186/s12859-023-05187-y" + licence: ["GPL-3.0-or-later"] + +input: + - - sample: + type: map + description: | + Groovy Map containing sample information + e.g. `[ id:'test' ]` + - metas: + type: list + description: A list of metadata maps corresponding to the input and output count files + - input_counts: + type: list + description: A list of paths to count files representing the baseline/input population + - output_counts: + type: list + description: A list of paths to count files representing the selection/output population + +output: + - merged_counts: + - sample: + type: map + description: Groovy Map containing sample information + - counts_merged.tsv: + type: file + description: A consolidated TSV file containing merged counts for all provided inputs and outputs + pattern: "counts_merged.tsv" + - versions: + - versions.yml: + type: file + description: File containing software versions + pattern: "versions.yml" + +authors: + - "@BenjaminWehnert1008" + - "@MaximilianStammnitz" diff --git a/modules/local/fitness/merge_counts/templates/merge_counts.R b/modules/local/fitness/merge_counts/templates/merge_counts.R new file mode 100644 index 0000000..8e39be8 --- /dev/null +++ b/modules/local/fitness/merge_counts/templates/merge_counts.R @@ -0,0 +1,112 @@ +#!/usr/bin/env Rscript + +# -------------------------------------------------------------- +# 1. SETUP & INPUTS +# -------------------------------------------------------------- + +raw_inputs_str <- "$input_counts" +raw_outputs_str <- "$output_counts" + +parse_paths <- function(x) { + if (x == "" || x == "[]") return(character(0)) + # Split at spaces + parts <- strsplit(x, "\\\\s+")[[1]] + # Remove empty strings + parts[parts != ""] +} + +input_paths <- parse_paths(raw_inputs_str) +output_paths <- parse_paths(raw_outputs_str) + +message("Found ", length(input_paths), " input files.") +message("Found ", length(output_paths), " output files.") + +# -------------------------------------------------------------- +# 2. MERGE LOGIC +# -------------------------------------------------------------- + +# Helper to read a 2-col TSV without header +read_counts <- function(fp) { + utils::read.table( + fp, header = FALSE, sep = "\\t", quote = "", + col.names = c("nt_seq", "count"), + colClasses = c("character", "numeric"), + comment.char = "", check.names = FALSE + ) +} + +# Read all inputs / outputs +input_list <- lapply(input_paths, read_counts) +output_list <- lapply(output_paths, read_counts) + +# Collect universe of sequences +all_seqs <- unique(c( + unlist(lapply(input_list, function(x) x\$nt_seq)), + unlist(lapply(output_list, function(x) x\$nt_seq)) +)) + +# Pre-allocate output frame +n_in <- length(input_list) +n_out <- length(output_list) + +col_names <- c( + "nt_seq", + if (n_in > 0) paste0("input", seq_len(n_in)) else character(0), + if (n_out > 0) paste0("output", seq_len(n_out)) else character(0) +) + +# Initialize dataframe with 0 counts +out <- data.frame( + nt_seq = all_seqs, + matrix(0, nrow = length(all_seqs), ncol = n_in + n_out), + stringsAsFactors = FALSE, check.names = FALSE +) +names(out) <- col_names + +# Fill inputs +if (n_in > 0) { + for (i in seq_len(n_in)) { + df <- input_list[[i]] + # Match sequences: WICHTIG \$ escaping + idx <- match(df\$nt_seq, out\$nt_seq) + # Assign counts: WICHTIG \$ escaping + out[idx, paste0("input", i)] <- df\$count + } +} + +# Fill outputs +if (n_out > 0) { + for (j in seq_len(n_out)) { + df <- output_list[[j]] + # WICHTIG \$ escaping + idx <- match(df\$nt_seq, out\$nt_seq) + out[idx, paste0("output", j)] <- df\$count + } +} + +# Write Output +utils::write.table( + out, + file = "counts_merged.tsv", + sep = "\\t", + row.names = FALSE, + col.names = TRUE, + quote = FALSE +) + +# -------------------------------------------------------------- +# 3. VERSIONING +# -------------------------------------------------------------- + +r_version <- strsplit(version[['version.string']], ' ')[[1]][3] +if (is.null(r_version)) r_version <- "unknown" + +f <- file("versions.yml", "w") +writeLines( + c( + '"${task.process}":', + paste(' r-base:', r_version) + ), + f +) +close(f) diff --git a/modules/local/fitness/run_dimsum/environment.yml b/modules/local/fitness/run_dimsum/environment.yml new file mode 100644 index 0000000..1c93270 --- /dev/null +++ b/modules/local/fitness/run_dimsum/environment.yml @@ -0,0 +1,15 @@ +channels: + - conda-forge + - bioconda +dependencies: + # Every pin here mirrors the paired container exactly. r-base must be pinned: with r-dimsum alone the + # solver picks an ancient R (4.0.5), not the container's 4.4.2. ggplot2/GGally must ALSO be pinned: + # DiMSum 1.4 builds its merge/diagnostics reports through GGally, which breaks under ggplot2 4.0 (the + # solver floats to 4.0.3 on R 4.4.2) with "object 'dimsum_meta_new_report' not found" and aborts the + # run; the container holds ggplot2 at 3.5.1 / GGally at 2.2.1, so we do the same. + - bioconda::r-dimsum=1.4 + - conda-forge::r-base=4.4.2 + - conda-forge::r-data.table=1.16.4 + - bioconda::bioconductor-biostrings=2.74.0 + - conda-forge::r-ggplot2=3.5.1 + - conda-forge::r-ggally=2.2.1 diff --git a/modules/local/fitness/run_dimsum/main.nf b/modules/local/fitness/run_dimsum/main.nf new file mode 100644 index 0000000..9c7d11c --- /dev/null +++ b/modules/local/fitness/run_dimsum/main.nf @@ -0,0 +1,54 @@ +process RUN_DIMSUM { + tag { sample.sample } + label 'process_single' + + conda "${moduleDir}/environment.yml" + + container "${ workflow.containerEngine == 'singularity' + ? 'https://community-cr-prod.seqera.io/docker/registry/v2/blobs/sha256/92/9298c391f285d7f2c1155f6a519a96c4f7591971780ba4f10569558282f40b6f/data' + : 'community.wave.seqera.io/library/bioconductor-biostrings_r-dimsum_r-base_r-data.table_pruned:cca13eed371a9d84' }" + + input: + tuple val(sample), path(counts_merged) + path(wt_txt) + path(exp_design) + + output: + path "dimsum_results**", emit: results_dir + path "versions.yml", emit: versions + + script: + """ + set -euo pipefail + + # DiMSum expects the sequence string, not a file path + WT=\$(tr -d ' \r\\n\\t' < "$wt_txt") + + DiMSum \ + --experimentDesignPath "$exp_design" \ + --wildtypeSequence "\$WT" \ + --countPath "$counts_merged" \ + --startStage 4 \ + --stopStage 5 \ + --fitnessErrorModel F \ + --retainIntermediateFiles T \ + --projectName "dimsum_results" \ + --fastqFileDir . \ + + R_VERSION=\$(R --version | head -n 1 | sed -E 's/^R version ([0-9.]+).*/\\1/') + cat <<-END_VERSIONS > versions.yml + DIMSUM_RUN: + r-base: \$R_VERSION +END_VERSIONS + """ + + stub: + """ + mkdir -p dimsum_results + touch dimsum_results/report.html + cat <<-END_VERSIONS > versions.yml + "${task.process}": + r-base: "0.0.0" + END_VERSIONS + """ +} diff --git a/modules/local/fitness/run_dimsum/meta.yml b/modules/local/fitness/run_dimsum/meta.yml new file mode 100644 index 0000000..3e889f9 --- /dev/null +++ b/modules/local/fitness/run_dimsum/meta.yml @@ -0,0 +1,49 @@ +name: "run_dimsum" +description: Runs DiMSum to estimate fitness and functionality scores from Deep Mutational Scanning (DMS) count data. +keywords: + - deep mutational scanning + - dms + - fitness + - dimsum +tools: + - "dimsum": + description: "An error-model-enriched pipeline for analyzing deep mutational scanning data" + homepage: "https://github.com/lehner-lab/DiMSum" + documentation: "https://github.com/lehner-lab/DiMSum/blob/master/README.md" + doi: "10.1186/s12859-020-03709-3" + licence: ["GPL-3.0-or-later"] + +input: + - - sample: + type: map + description: | + Groovy Map containing sample information. + Matches the structure used in the pipeline (e.g., [ id:'test' ]) + - counts_merged: + type: file + description: A TSV file containing variant counts merged across samples + pattern: "*.{tsv,csv}" + - - wt_txt: + type: file + description: A text file containing the wild-type DNA/amino acid sequence string + pattern: "*.{txt}" + - - exp_design: + type: file + description: DiMSum-specific experimental design file describing replicates and conditions + pattern: "*.{txt,csv}" + +output: + - results_dir: + - dimsum_results**: + type: directory + description: Directory containing the full suite of DiMSum output files, including fitness estimates and plots + pattern: "dimsum_results*" + - versions: + - versions.yml: + type: file + description: File containing software versions + pattern: "versions.yml" + +authors: + - "@BenjaminWehnert1008" + - "@MaximilianStammnitz" diff --git a/modules/local/fitness/run_mutscan/environment.yml b/modules/local/fitness/run_mutscan/environment.yml new file mode 100644 index 0000000..a8b1c61 --- /dev/null +++ b/modules/local/fitness/run_mutscan/environment.yml @@ -0,0 +1,7 @@ +channels: + - conda-forge + - bioconda +dependencies: + - bioconda::bioconductor-biostrings=2.78.0 + - bioconda::bioconductor-mutscan=1.0.0 + - conda-forge::r-base=4.5.1 diff --git a/modules/local/fitness/run_mutscan/main.nf b/modules/local/fitness/run_mutscan/main.nf new file mode 100644 index 0000000..3024b0b --- /dev/null +++ b/modules/local/fitness/run_mutscan/main.nf @@ -0,0 +1,33 @@ +process RUN_MUTSCAN { + tag "${sample.sample}" + label 'process_medium' + + conda "${moduleDir}/environment.yml" + + container "${ workflow.containerEngine == 'singularity' + ? 'https://community-cr-prod.seqera.io/docker/registry/v2/blobs/sha256/73/73a72ec77725aeb67678a74228938fdd6827b669d01a8c96951b1a8ef96eeb0f/data' + : 'community.wave.seqera.io/library/bioconductor-biostrings_bioconductor-mutscan_r-base_r-biocmanager_pruned:c65036d76406f342' }" + + input: + tuple val(sample), path(counts_merged) + path(syn_wt_txt) + path(exp_design) + + output: + tuple val(sample), path("fitness_estimation_mutscan_edgeR.tsv"), emit: fitness_mutscan_edgeR + tuple val(sample), path("fitness_estimation_mutscan_limma.tsv"), emit: fitness_mutscan_limma + tuple val(sample), path("*.pdf"), emit: qc_plots + path "versions.yml", emit: versions + + script: + template 'fitness_calculation_mutscan.R' + + stub: + """ + touch fitness_estimation_mutscan_edgeR.tsv fitness_estimation_mutscan_limma.tsv mutscan_stub.pdf + cat <<-END_VERSIONS > versions.yml + "${task.process}": + r-base: "0.0.0" + END_VERSIONS + """ +} diff --git a/modules/local/fitness/run_mutscan/meta.yml b/modules/local/fitness/run_mutscan/meta.yml new file mode 100644 index 0000000..f80e038 --- /dev/null +++ b/modules/local/fitness/run_mutscan/meta.yml @@ -0,0 +1,81 @@ +name: "run_mutscan" +description: Calculates fitness and functionality scores using the mutscan R package, employing edgeR and limma-voom statistical frameworks for robust estimation from input and output variant counts. +keywords: + - deep mutational scanning + - dms + - fitness estimation + - mutscan + - edger + - limma +tools: + - "mutscan": + description: "R package for analysis of deep mutational scanning data" + homepage: "https://bioconductor.org/packages/release/bioc/html/mutscan.html" + documentation: "https://bioconductor.org/packages/release/bioc/manuals/mutscan/man/mutscan.pdf" + tool_dev_url: "https://github.com/csoneson/mutscan" + doi: "10.1186/s12859-023-05187-y" + licence: ["GPL-3.0-or-later"] + - "edger": + description: "Empirical Analysis of Digital Gene Expression Data in R" + homepage: "https://bioconductor.org/packages/release/bioc/html/edgeR.html" + doi: "10.1093/bioinformatics/btp616" + licence: ["GPL-2.0-or-later"] + - "limma": + description: "Linear Models for Microarray Data" + homepage: "https://bioconductor.org/packages/release/bioc/html/limma.html" + doi: "10.1093/nar/gkv007" + licence: ["GPL-2.0-or-later"] + +input: + - - sample: + type: map + description: | + Groovy Map containing sample information + e.g. `[ id:'test' ]` + - counts_merged: + type: file + description: TSV/CSV file containing variant counts merged across samples or replicates + pattern: "*.{tsv,csv}" + - - syn_wt_txt: + type: file + description: Text file identifying the synonymous wild-type mutation used as the reference baseline + pattern: "*.txt" + - - exp_design: + type: file + description: Experimental design file defining replicates, conditions, and groupings for statistical analysis + pattern: "*.{csv,txt}" + +output: + - fitness_mutscan_edgeR: + - sample: + type: map + description: Groovy Map containing sample information + - fitness_estimation_mutscan_edgeR.tsv: + type: file + description: Fitness estimation results generated using the edgeR framework + pattern: "fitness_estimation_mutscan_edgeR.tsv" + - fitness_mutscan_limma: + - sample: + type: map + description: Groovy Map containing sample information + - fitness_estimation_mutscan_limma.tsv: + type: file + description: Fitness estimation results generated using the limma framework + pattern: "fitness_estimation_mutscan_limma.tsv" + - qc_plots: + - sample: + type: map + description: Groovy Map containing sample information + - "*.pdf": + type: file + description: Quality control plots generated by mutscan during fitness estimation + pattern: "*.pdf" + - versions: + - versions.yml: + type: file + description: File containing software versions + pattern: "versions.yml" + +authors: + - "@BenjaminWehnert1008" + - "@MaximilianStammnitz" diff --git a/modules/local/fitness/run_mutscan/templates/fitness_calculation_mutscan.R b/modules/local/fitness/run_mutscan/templates/fitness_calculation_mutscan.R new file mode 100644 index 0000000..8a196ff --- /dev/null +++ b/modules/local/fitness/run_mutscan/templates/fitness_calculation_mutscan.R @@ -0,0 +1,371 @@ +#!/usr/bin/env Rscript + +## mutscan (Soneson et al., Genome Biology 2023) fitness estimation for nf-core/deepmutscan +## 19.03.2026 + +## 0. Libraries ## +################## + +suppressPackageStartupMessages({ + library(mutscan) + library(Biostrings) +}) + +## --- Helper functions --- + +# calculate nt hamming distances from the specified WT +nbrMutBases <- function(x, wt.seq) { + x <- cbind(x, "nbrMutBases" = rep(NA, nrow(x))) + for (i in 1:nrow(x)){ + tmp.wt <- strsplit(as.character(wt.seq), "")[[1]] + tmp.mut <- strsplit(as.character(x\$sequence[i]), "")[[1]] + if(length(which(tmp.mut != tmp.wt)) == 0){ + x\$nbrMutBases[i] <- 0 + rm(tmp.mut, tmp.wt) + next + }else{ + x\$nbrMutBases[i] <- length(which(tmp.mut != tmp.wt)) + rm(tmp.mut, tmp.wt) + next + } + } + x\$nbrMutBases <- as.character(x\$nbrMutBases) + return(x) +} + +# calculate codon hamming distances from the specified WT +nbrMutCodons <- function(x, wt.seq) { + x <- cbind(x, "nbrMutCodons" = rep(NA, nrow(x))) + for (i in 1:nrow(x)){ + tmp.wt <- strsplit(as.character(wt.seq), "")[[1]] + tmp.mut <- strsplit(as.character(x\$sequence[i]), "")[[1]] + if(length(which(tmp.mut != tmp.wt)) == 0){ + x\$nbrMutCodons[i] <- 0 + rm(tmp.mut, tmp.wt) + next + }else if(length(which(tmp.mut != tmp.wt)) == 1){ + x\$nbrMutCodons[i] <- 1 + rm(tmp.mut, tmp.wt) + }else if(length(which(tmp.mut != tmp.wt)) > 1){ + mut.pos <- which(tmp.mut != tmp.wt) + x\$nbrMutCodons[i] <- length(unique(ceiling(mut.pos / 3))) + rm(tmp.mut, tmp.wt) + next + } + } + x\$nbrMutCodons <- as.character(x\$nbrMutCodons) + return(x) +} + +# translate sequences and add aa_seq +sequenceAA <- function(x) { + x <- cbind(x, "sequenceAA" = as.character(translate(DNAStringSet(x\$sequence)))) + return(x) +} + +# calculate AA hamming distances from the WT +nbrMutAAs <- function(x, wt.seq) { + x <- cbind(x, "nbrMutAAs" = rep(NA, nrow(x))) + for (i in 1:nrow(x)){ + tmp.wt <- strsplit(as.character(translate(wt.seq)), "")[[1]] + tmp.mut <- strsplit(as.character(x\$sequenceAA[i]), "")[[1]] + if(length(which(tmp.mut != tmp.wt)) == 0){ + x\$nbrMutAAs[i] <- 0 + rm(tmp.mut, tmp.wt) + next + }else{ + x\$nbrMutAAs[i] <- length(which(tmp.mut != tmp.wt)) + rm(tmp.mut, tmp.wt) + next + } + } + x\$nbrMutAAs <- as.character(x\$nbrMutAAs) + return(x) +} + +# mutant base labels +mutantNameBase <- function(x, wt.seq){ + x <- cbind(x, "mutantNameBase" = rep(NA, nrow(x))) + for(i in 1:nrow(x)){ + tmp.wt <- strsplit(as.character(wt.seq), "")[[1]] + tmp.mut <- strsplit(as.character(x\$sequence[i]), "")[[1]] + if(length(which(tmp.mut != tmp.wt)) == 0){ + x\$mutantNameBase[i] <- "f.0.WT" + rm(tmp.mut, tmp.wt) + next + }else if(length(which(tmp.mut != tmp.wt)) > 0){ + mut.pos <- which(tmp.mut != tmp.wt) + x\$mutantNameBase[i] <- paste0("f.", mut.pos, ".", tmp.mut[mut.pos], collapse = "_") + rm(tmp.mut, tmp.wt, mut.pos) + next + } + } + return(x) +} + +# mutant codon labels +mutantNameCodon <- function(x, wt.seq){ + x <- cbind(x, "mutantNameCodon" = rep(NA, nrow(x))) + for(i in 1:nrow(x)){ + tmp.wt <- strsplit(as.character(wt.seq), "(?<=.{3})", perl = TRUE)[[1]] + tmp.mut <- strsplit(as.character(x\$sequence[i]), "(?<=.{3})", perl = TRUE)[[1]] + if(length(which(tmp.mut != tmp.wt)) == 0){ + x\$mutantNameCodon[i] <- "f.0.WT" + rm(tmp.mut, tmp.wt) + next + }else if(length(which(tmp.mut != tmp.wt)) > 0){ + mut.pos <- which(tmp.mut != tmp.wt) + x\$mutantNameCodon[i] <- paste0("f.", mut.pos, ".", tmp.mut[mut.pos], collapse = "_") + rm(tmp.mut, tmp.wt, mut.pos) + next + } + } + return(x) +} + +# name the mutations (AA level) +mutantNameAA <- function(x, wt.seq) { + x <- cbind(x, "mutantNameAA" = rep(NA, nrow(x))) + for (i in 1:nrow(x)){ + if(x\$nbrMutAAs[i] == 0){ + x\$mutantNameAA[i] <- "f.0.WT" + }else{ + tmp.wt <- strsplit(as.character(translate(wt.seq)), "")[[1]] + tmp.mut <- strsplit(as.character(x\$sequenceAA[i]), "")[[1]] + tmp.pos <- which(tmp.mut != tmp.wt) + x\$mutantNameAA[i] <- paste0("f.", tmp.pos, ".", tmp.mut[tmp.pos]) + rm(tmp.mut, tmp.wt,tmp.pos) + } + } + return(x) +} + +# categorise the mutation types +mutationTypes <- function(x){ + x <- cbind(x, "mutationTypes" = rep(NA, nrow(x))) + x[grep("WT", x\$mutantNameAA), "mutationTypes"] <- "silent" ## silent/synonymous + x[grep("[*]", x\$mutantNameAA), "mutationTypes"] <- "stop" ## stop + x[-grep("[*]|WT", x\$mutantNameAA), "mutationTypes"] <- "nonsynonymous" ## nonsynonymous + x +} + +# mutant name +mutantName <- function(x, wt.seq){ + x <- cbind("mutantName" = rep(NA, nrow(x)), x) + for (i in 1:nrow(x)){ + if(x\$nbrMutAAs[i] == 0){ + x\$mutantName[i] <- "WT" + }else{ + tmp.wt <- strsplit(as.character(translate(wt.seq)), "")[[1]] + tmp.mut <- strsplit(as.character(x\$sequenceAA[i]), "")[[1]] + tmp.pos <- which(tmp.mut != tmp.wt) + x\$mutantName[i] <- paste0(tmp.wt[tmp.pos], tmp.pos,tmp.mut[tmp.pos]) + rm(tmp.mut, tmp.wt,tmp.pos) + } + } + return(x) +} + +# mutscan 'summaryTable' building (from merged counts TSV file) +mutscan.summaryTable.from.counts <- function(x, sample, wt.seq){ + + ### pre-process + x <- x[,c(1, grep(sample, colnames(x)))] + colnames(x) <- c("sequence", "nbrReads") + x <- cbind(x, "maxNbrReads" = x[,"nbrReads"], "nbrUmis" = x[,"nbrReads"]) + + ### nbrMutBases + x <- nbrMutBases(x, wt.seq) + + ### nbrMutCodons + x <- nbrMutCodons(x, wt.seq) + + ### sequenceAA + x <- sequenceAA(x) + + ### nbrMutAAs + x <- nbrMutAAs(x, wt.seq) + + ### varLengths + x <- cbind(x, "varLengths" = as.character(nchar(x\$sequence))) + + ### mutantNameBase + x <- mutantNameBase(x, wt.seq) + + ### mutantNameCodon + x <- mutantNameCodon(x, wt.seq) + + ### mutantNameBaseHGVS (would ideally look like this: "f:c", "f:c.32_33delinsAC", etc.) + x <- cbind(x, "mutantNameBaseHGVS" = x\$mutantNameCodon) + + ### mutantNameAA + x <- mutantNameAA(x, wt.seq) + + ### mutantNameAAHGVS (would ideally look like this: "f:p" or "f:p.(Leu11His)") + x <- cbind(x, "mutantNameAAHGVS" = x\$mutantNameAA) + + ### mutationTypes: silent, stop, nonsynonymous + x <- mutationTypes(x) + + ### mutantName + x <- mutantName(x, wt.seq) + + ### re-order columns + x <- x[,c(1:7,9:16,8)] + + ### output + return(x) +} + +## --- Main function --- + +#' Run mutscan fitness estimation with configurable I/O paths +#' +#' @param counts_path Path to counts_merged.tsv +#' @param design_path Path to experimentalDesign.tsv +#' @param wt_seq_path Path to synonymous_wt.txt (single line DNA sequence) +#' @param output_path Path to write fitness_estimation.tsv +#' +#' @return Invisibly returns the final data.frame; writes the output to output_path. +run_mutscan_fitness_estimation <- function(counts_path, + design_path, + wt_seq_path, + output_path_edgeR, + output_path_limma){ + + ## 1. Import key files ## + ######################### + + merged.counts <- read.table(counts_path, sep = "\t", header = T, check.names = F) + exp.design <- read.table(design_path, sep = "\t", header = T, check.names = F) + wt.seq <- DNAString(as.character(read.table(wt_seq_path))) + + ## 2. Variant count matrix reformatting ## + ########################################## + + var.tables <- vector(mode = "list", length = nrow(exp.design)) + names(var.tables) <- exp.design[,"sample_name"] + var.tables <- lapply(var.tables, function(x){x <- vector(mode = "list", length = 4); + names(x) <- c("summaryTable", "filterSummary", "errorStatistics", "parameters"); + return(x)}) + + # mutscan 'summaryTable' + for(i in 1:length(var.tables)){ + print(i) + var.tables[[i]]\$summaryTable <- mutscan.summaryTable.from.counts(merged.counts, names(var.tables)[i], wt.seq) + } + + # mutscan 'filterSummary' (fill with minimal decoy) + var.tables <- lapply(var.tables, function(x){x\$filterSummary <- data.frame(NA); return(x)}) + + # mutscan 'errorStatistics' (fill with minimal decoy) + var.tables <- lapply(var.tables, function(x){x\$errorStatistics <- NA; return(x)}) + + # mutscan 'parameters' (fill with minimal decoy) + var.tables <- lapply(var.tables, function(x){x\$parameters\$mutNameDelimiter <- '.'; return(x)}) + + ## 3. summarizeExperiment object ## + ################################### + + # mutscan 'coldata' object (from experimental design TSV file) + condition <- exp.design\$selection_id + condition[which(condition == '0')] <- "input" + condition[which(condition == '1')] <- "output" + coldata <- as.data.frame(cbind("Name" = exp.design\$sample_name, + "Condition" = condition, + "Replicate" = exp.design\$experiment_replicate)) + class(coldata\$Replicate) <- "integer" + + # mutscan 'summarizeExperiment' object + se <- summarizeExperiment(x = var.tables, + coldata = coldata, + countType = "reads") + + ## 4. logFC calculations ## + ########################### + + # mutscan 'model.matrix' object to calculate logFC values + model.design <- model.matrix(~ Replicate + Condition, data = se@colData) + + # edgeR + logFC.edgeR <- calculateRelativeFC(se = se, + design = model.design, + coef = "Conditionoutput", + WTrows = "WT", + selAssay = "counts", + pseudocount = 1, + method = "edgeR") + + # limma + logFC.limma <- calculateRelativeFC(se = se, + design = model.design, + coef = "Conditionoutput", + WTrows = "WT", + selAssay = "counts", + pseudocount = 1, + method = "limma") + + ## 5. QC plots ## + ################# + + # raw counts comparison + pdf('mutscan_counts_corr.pdf', height = 9, width = 14) + print(plotPairs(se, selAssay = "counts", addIdentityLine = TRUE)) + dev.off() + + # edgeR volcano plot + pdf('mutscan_edgeR_volcano.pdf', height = 9, width = 14) + print(plotVolcano(logFC.edgeR, pointSize = "large")) + dev.off() + + # limma volcano plot + pdf('mutscan_limma_volcano.pdf', height = 9, width = 14) + print(plotVolcano(logFC.limma, pointSize = "large")) + dev.off() + + ## 6. Data export ## + #################### + + write.table(logFC.edgeR, output_path_edgeR, + col.names = TRUE, row.names = FALSE, quote = FALSE, sep = "\t", na = "") + write.table(logFC.limma, output_path_limma, + col.names = TRUE, row.names = FALSE, quote = FALSE, sep = "\t", na = "") + + invisible(logFC.edgeR) + invisible(logFC.limma) + +} + +##### +# run function +##### +run_mutscan_fitness_estimation( + counts_path = "$counts_merged", + design_path = "$exp_design", + wt_seq_path = "$syn_wt_txt", + output_path_edgeR = "fitness_estimation_mutscan_edgeR.tsv", + output_path_limma = "fitness_estimation_mutscan_limma.tsv" +) + +#### +# create versions.yml +#### +r_version <- strsplit(version[['version.string']], ' ')[[1]][3] +mutscan_version <- as.character(packageVersion("mutscan")) +Biostrings_version <- as.character(packageVersion("Biostrings")) + +if (is.null(r_version)) r_version <- "unknown" +if (length(mutscan_version) == 0) mutscan_version <- "unknown" +if (length(Biostrings_version) == 0) Biostrings_version <- "unknown" + +f <- file("versions.yml", "w") +writeLines( + c( + '"${task.process}":', + paste(' r-base:', r_version), + paste(' r-mutscan:', mutscan_version), + paste(' r-Biostrings:', Biostrings_version) + ), + f +) +close(f) diff --git a/modules/local/structure/variant_effect_inspection_tool/environment.yml b/modules/local/structure/variant_effect_inspection_tool/environment.yml new file mode 100644 index 0000000..a72f7ec --- /dev/null +++ b/modules/local/structure/variant_effect_inspection_tool/environment.yml @@ -0,0 +1,7 @@ +channels: + - conda-forge + - bioconda +dependencies: + - conda-forge::python=3.12.3 + - conda-forge::pandas=2.2.1 + - conda-forge::numpy=1.26.4 diff --git a/modules/local/structure/variant_effect_inspection_tool/main.nf b/modules/local/structure/variant_effect_inspection_tool/main.nf new file mode 100644 index 0000000..8935225 --- /dev/null +++ b/modules/local/structure/variant_effect_inspection_tool/main.nf @@ -0,0 +1,42 @@ +// Build the single, self-contained interactive variant effect inspection tool (HTML). +// Aggregates per-residue metrics (fitness, pLDDT accuracy, coverage, counts, counts/cov, error bias), +// aligns the wildtype sequence onto the structure, and inlines 3Dmol.js + PDB + data into +// one portable HTML file (plus a structure_data.json sidecar). +process VARIANT_EFFECT_INSPECTION_TOOL { + tag "${meta.sample ?: meta.id}" + label 'process_single' + + conda "${moduleDir}/environment.yml" + + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container + ? 'https://depot.galaxyproject.org/singularity/pandas:2.2.1' + : 'quay.io/biocontainers/pandas:2.2.1' }" + + input: + tuple val(meta), path(pdb), path(fitness_tsv) + path(variant_counts, stageAs: 'vc*/*') // per-sample files share a basename -> stage in numbered dirs + path aa_seq + path threedmol_js + path template_html + + output: + tuple val(meta), path("variant_effect_inspection_tool.html"), emit: viewer + tuple val(meta), path("structure_data.json") , emit: data + path "versions.yml" , emit: versions + + when: + task.ext.when == null || task.ext.when + + script: + template 'build_variant_effect_inspection_tool.py' + + stub: + """ + touch variant_effect_inspection_tool.html structure_data.json + cat <<-END_VERSIONS > versions.yml + "${task.process}": + python: "0.0.0" + pandas: "0.0.0" + END_VERSIONS + """ +} diff --git a/modules/local/structure/variant_effect_inspection_tool/meta.yml b/modules/local/structure/variant_effect_inspection_tool/meta.yml new file mode 100644 index 0000000..5582e00 --- /dev/null +++ b/modules/local/structure/variant_effect_inspection_tool/meta.yml @@ -0,0 +1,75 @@ +name: "variant_effect_inspection_tool" +description: Build a single, self-contained interactive variant effect inspection tool (HTML) that projects per-residue fitness, counts, coverage and error-correction bias onto the wildtype 3D structure using 3Dmol.js. +keywords: + - deep mutational scanning + - variant effect + - 3D structure + - visualisation +tools: + - "3Dmol.js": + description: "Object-oriented, WebGL-based JavaScript library for molecular visualisation." + homepage: "https://3dmol.csb.pitt.edu/" + documentation: "https://3dmol.csb.pitt.edu/doc/" + licence: ["BSD-3-Clause"] + - "pandas": + description: "Fast, powerful data structures for data analysis in Python." + homepage: "https://pandas.pydata.org/" + licence: ["BSD-3-Clause"] + +input: + - - meta: + type: map + description: | + Groovy Map containing sample information + e.g. `[ id:'test', sample:'ORF1' ]` + - pdb: + type: file + description: Wildtype 3D structure (PDB); per-residue confidence read from the B-factor column. + pattern: "*.pdb" + - fitness_tsv: + type: file + description: Per-sample default fitness estimation table. + pattern: "*.tsv" + - - variant_counts: + type: file + description: Per-sample (error-corrected) library-filtered variant count tables. + pattern: "*.csv" + - - aa_seq: + type: file + description: Reference amino-acid sequence (one line). + - - threedmol_js: + type: file + description: Bundled 3Dmol.js library (inlined into the HTML). + pattern: "*.js" + - - template_html: + type: file + description: HTML template into which the structure, data and 3Dmol.js are injected. + pattern: "*.html" + +output: + - viewer: + - meta: + type: map + description: Groovy Map containing sample information + - variant_effect_inspection_tool.html: + type: file + description: Self-contained interactive variant effect inspection tool. + pattern: "variant_effect_inspection_tool.html" + - data: + - meta: + type: map + description: Groovy Map containing sample information + - structure_data.json: + type: file + description: Sidecar with the per-residue/per-variant data projected onto the structure. + pattern: "structure_data.json" + - versions: + - versions.yml: + type: file + description: File containing software versions + pattern: "versions.yml" + +authors: + - "@BenjaminWehnert1008" +maintainers: + - "@BenjaminWehnert1008" diff --git a/modules/local/structure/variant_effect_inspection_tool/templates/build_variant_effect_inspection_tool.py b/modules/local/structure/variant_effect_inspection_tool/templates/build_variant_effect_inspection_tool.py new file mode 100755 index 0000000..227a56e --- /dev/null +++ b/modules/local/structure/variant_effect_inspection_tool/templates/build_variant_effect_inspection_tool.py @@ -0,0 +1,334 @@ +#!/usr/bin/env python3 +""" +Build a single self-contained, interactive variant effect inspection tool (HTML) for nf-core/deepmutscan. + +Nextflow ``template`` script for the VARIANT_EFFECT_INSPECTION_TOOL module (staged from this module's +``templates/`` folder). Reads the wildtype structure (PDB), the fitness estimation table and the +per-sample variant-count tables, aggregates per-residue/per-variant metrics, and writes one portable +HTML file with 3Dmol.js, the PDB coordinates and all data inlined (no external/CDN/sidecar +dependency), plus a structure_data.json sidecar and versions.yml. + +Note: this is a Nextflow template, so input paths / the sample name are filled in as Nextflow +variables and every backslash meant for Python is written doubled (as in the pipeline's R templates). +""" +import json +import platform +import re +import sys +from pathlib import Path + +import numpy as np +import pandas as pd + +THREE_TO_ONE = { + "ALA": "A", "ARG": "R", "ASN": "N", "ASP": "D", "CYS": "C", "GLN": "Q", "GLU": "E", + "GLY": "G", "HIS": "H", "ILE": "I", "LEU": "L", "LYS": "K", "MET": "M", "PHE": "F", + "PRO": "P", "SER": "S", "THR": "T", "TRP": "W", "TYR": "Y", "VAL": "V", +} +AA_ORDER = list("ACDEFGHIKLMNPQRSTVWY") +MUT_RE = re.compile(r"^([A-Z])(\\d+)([A-Z*])\$") + + +def parse_pdb(pdb_path): + """Return (pdb_text, residues) where residues = [{resnum, aa, plddt}] from CA atoms.""" + text = Path(pdb_path).read_text() + residues = [] + seen = set() + for line in text.splitlines(): + if not line.startswith("ATOM"): + continue + atom_name = line[12:16].strip() + if atom_name != "CA": + continue + try: + resnum = int(line[22:26]) + bfac = float(line[60:66]) + except ValueError: + continue + if resnum in seen: + continue + seen.add(resnum) + resname = line[17:20].strip().upper() + residues.append({"resnum": resnum, "aa": THREE_TO_ONE.get(resname, "X"), "plddt": bfac}) + residues.sort(key=lambda r: r["resnum"]) + return text, residues + + +def best_alignment(wt_seq, struct_residues): + """Find offset so that wt position i (1-based) maps to struct_residues[off + i - 1]. + + Scans all offsets, maximising identity. Returns (offset0, identity_fraction) where the + structure index for wt index j (0-based) is off0 + j. + """ + struct_seq = "".join(r["aa"] for r in struct_residues) + n, m = len(wt_seq), len(struct_seq) + best_off, best_matches = 0, -1 + for off in range(-n + 1, m): + matches = 0 + for j in range(n): + k = off + j + if 0 <= k < m and struct_seq[k] == wt_seq[j]: + matches += 1 + if matches > best_matches: + best_matches, best_off = matches, off + identity = best_matches / max(1, n) + return best_off, identity + + +def r6(x): + """Round to 6 dp so output is byte-stable regardless of floating-point summation order.""" + return round(x, 6) if isinstance(x, (int, float)) else x + + +def to_float(x): + try: + f = float(x) + return f if f == f else None # drop NaN + except (TypeError, ValueError): + return None + + +REP_RE = re.compile(r"^rescaled_fitness_rep(\\d+)\$", re.I) +OUT_RE = re.compile(r"^output(\\d+)\$", re.I) + + +def load_fitness(path): + """Per-position variant records from the default fitness_estimation.tsv. + + Captures the mean fitness, its SD and every per-replicate (rescaled) fitness, so the viewer can + recompute the mean/SD over a user-chosen subset of replicates. That subsetting is exact rather + than approximate: the fitness step derives each `rescaled_fitness_rep` from that replicate's + own input/output counts and its own synonymous/stop anchors, so a replicate's value never + depends on which other replicates were in the run. + + Returns (per_pos, rep_ids). Any number of replicates is handled; missing values are tolerated. + """ + df = pd.read_csv(path, sep="\\t", dtype=str, keep_default_na=False) + + def col(name): + return next((c for c in df.columns if c.strip().lower() == name), None) + + def numbered(rx): + return sorted((c for c in df.columns if rx.match(c.strip())), + key=lambda c: int(rx.match(c.strip()).group(1))) + + fit_col, sd_col = col("mean fitness"), col("fitness sd") + rep_cols = numbered(REP_RE) + out_cols = numbered(OUT_RE) + rep_ids = [REP_RE.match(c.strip()).group(1) for c in rep_cols] + + per_pos = {} + for _, row in df.iterrows(): + pos = to_float(row.get("pos", "")) + wt = (row.get("wt_aa", "") or "").strip() + mut = (row.get("mut_aa", "") or "").strip() + if pos is None or not wt or not mut: + continue + pos = int(pos) + is_stop = mut == "*" or (row.get("stop", "").strip() not in ("", "0")) + per_pos.setdefault(pos, {"wt": wt, "variants": []})["variants"].append({ + "mut": mut, + "fitness": to_float(row.get(fit_col, "")) if fit_col else None, + "sd": to_float(row.get(sd_col, "")) if sd_col else None, + "reps": [to_float(row.get(c, "")) for c in rep_cols], + "n_out": sum((to_float(row.get(c, "")) or 0) for c in out_cols), + "syn": mut == wt, "stop": bool(is_stop), + }) + return per_pos, rep_ids + + +def load_counts(paths): + """Aggregate coverage / counts / counts_per_cov across all sample CSVs. + + Returns two dicts: per position ``{pos: {...}}`` (used to colour the structure) and per variant + ``{(pos, mut): {...}}`` (used for the substitution-specific bee-swarm distributions). + """ + frames = [] + for p in paths: + try: + frames.append(pd.read_csv(p)) + except Exception as exc: # pragma: no cover - defensive + print(f"WARN: could not read {p}: {exc}", file=sys.stderr) + if not frames: + return {}, {} + df = pd.concat(frames, ignore_index=True) + # The "A1M"-style WT/pos/mut label lives in the `pos_mut` column + # (`aa_mut` holds e.g. "M:A>M"). Fall back to aa_mut only if pos_mut is absent. + label_col = "pos_mut" if "pos_mut" in df.columns else ("aa_mut" if "aa_mut" in df.columns else None) + if label_col is None: + return {}, {} + cols = (("coverage", "cov"), ("counts", "counts"), ("counts_per_cov", "counts_per_cov"), + ("counts_raw", "counts_raw")) + pos_acc, var_acc = {}, {} + for _, row in df.iterrows(): + m = MUT_RE.match(str(row.get(label_col, "")).strip()) + if not m: + continue + pos, mut = int(m.group(2)), m.group(3) + pa = pos_acc.setdefault(pos, {k: [] for k, _ in cols}) + va = var_acc.setdefault((pos, mut), {k: [] for k, _ in cols}) + for key, src in cols: + v = to_float(row.get(src, "")) + if v is not None: + pa[key].append(v) + va[key].append(v) + + def meaned(acc): + return {k: {kk: (sum(vv) / len(vv) if vv else None) for kk, vv in a.items()} + for k, a in acc.items()} + + return meaned(pos_acc), meaned(var_acc) + + +class args: + # Filled in by Nextflow when this template is staged and run by the VARIANT_EFFECT_INSPECTION_TOOL process. + pdb = "$pdb" + fitness = "$fitness_tsv" + aa_seq = "$aa_seq" + counts = "$variant_counts".split() # collected per-sample CSVs (staged as vc*/*) + threedmol = "$threedmol_js" + template = "$template_html" + sample = "${meta.sample ?: meta.id}" + out_html = "variant_effect_inspection_tool.html" + out_json = "structure_data.json" + + +def main(): + + wt_seq = Path(args.aa_seq).read_text().strip().replace("\\n", "").replace("\\r", "") + pdb_text, struct_residues = parse_pdb(args.pdb) + off0, identity = best_alignment(wt_seq, struct_residues) + struct_by_index = struct_residues # 0-based list + + fit_per_pos, rep_ids = load_fitness(args.fitness) + # per-position aggregate (structure colour) and per-variant values (substitution-specific swarm) + counts_per_pos, counts_per_var = load_counts(sorted(args.counts)) # sorted -> deterministic + + # ---- candidate metrics (dropped automatically when a dataset has no data for them) ---- + # scope="variant": has a per-substitution value (used in the bee-swarm); the position mean is + # still what colours the structure. reverse=True flips the colour ramp (low -> red, high -> blue). + METRIC_SPECS = [ + ("fitness", dict(label="Fitness (nf-core/deepmutscan)", type="diverging", scope="variant", + reverse=True, + desc="Fitness per substitution; the position mean is shown on the structure")), + ("fitness_sd", dict(label="Fitness uncertainty (SD)", type="sequential", scope="variant", + desc="Standard deviation of the fitness estimate (lower = more confident)")), + ("accuracy", dict(label="Prediction accuracy (pLDDT)", type="plddt", scope="residue", fixed=[0, 100], + desc="Per-residue model confidence from the structure B-factor")), + ("coverage", dict(label="Coverage", type="sequential", scope="variant", + desc="Sequencing coverage of this residue (the same for all substitutions at one residue)")), + ("counts", dict(label="Counts", type="sequential", scope="variant", + desc="Variant read counts per substitution (position mean shown on the structure)")), + ("counts_per_cov", dict(label="Counts / coverage", type="sequential", scope="variant", + desc="Counts normalised by coverage, per substitution (position mean shown on the structure)")), + ("n_substitutions", dict(label="Measured substitutions", type="sequential", scope="residue", + desc="Number of substitutions with a measured fitness at the position")), + ("error_bias", dict(label="Positional error bias", type="sequential", scope="residue", + desc="Fraction of counts removed by sequencing-error correction at this position")), + ] + variant_metrics = {k for k, s in METRIC_SPECS if s["scope"] == "variant"} + + def mean(xs): + return (sum(xs) / len(xs)) if xs else None + + residues = [] + for i, wt_aa in enumerate(wt_seq): # i is 0-based; position = i + 1 + pos = i + 1 + k = off0 + i + srec = struct_by_index[k] if 0 <= k < len(struct_by_index) else None + variants = fit_per_pos.get(pos, {}).get("variants", []) + miss = [v for v in variants if not v["syn"] and not v["stop"]] + fit_vals = [v["fitness"] for v in miss if v["fitness"] is not None] + sd_vals = [v["sd"] for v in miss if v["sd"] is not None] + cp = counts_per_pos.get(pos, {}) + craw, cnow = cp.get("counts_raw"), cp.get("counts") + error_bias = (1.0 - cnow / craw) if (craw not in (None, 0) and cnow is not None) else None + res_metrics = { + "fitness": r6(mean(fit_vals)), + "fitness_sd": r6(mean(sd_vals)), + "accuracy": r6(srec["plddt"]) if srec else None, + "coverage": r6(cp.get("coverage")), + "counts": r6(cp.get("counts")), + "counts_per_cov": r6(cp.get("counts_per_cov")), + "n_substitutions": (len(fit_vals) or None), + "error_bias": r6(error_bias), + } + vlist = [] + for v in sorted(variants, key=lambda v: AA_ORDER.index(v["mut"]) if v["mut"] in AA_ORDER else 99): + vc = counts_per_var.get((pos, v["mut"]), {}) + vlist.append({ + "mut": v["mut"], "syn": v["syn"], "stop": v["stop"], "n_out": r6(v["n_out"]), + # per-replicate fitness, so the viewer can re-derive mean/SD for a chosen subset + "reps": [r6(x) if x is not None else None for x in v["reps"]], + "metrics": { + "fitness": r6(v["fitness"]), "fitness_sd": r6(v["sd"]), + "coverage": r6(vc.get("coverage")), "counts": r6(vc.get("counts")), + "counts_per_cov": r6(vc.get("counts_per_cov")), + }, + }) + residues.append({ + "pos": pos, "wt": wt_aa, + "resnum": srec["resnum"] if srec else None, + "mapped": bool(srec and srec["aa"] == wt_aa), + "metrics": res_metrics, "variants": vlist, + }) + + # keep only metrics that actually have data (generality across diverse datasets) + metrics = {} + for key, spec in METRIC_SPECS: + vals = [r["metrics"].get(key) for r in residues if r["metrics"].get(key) is not None] + if not vals: + continue + if "fixed" in spec: + mrange = spec["fixed"] + elif spec["type"] == "diverging": + a = max(abs(min(vals)), abs(max(vals))) or 1 + mrange = [r6(-a), r6(a)] + else: + lo, hi = min(vals), max(vals) + mrange = [r6(lo), r6(hi if hi != lo else lo + 1)] + metrics[key] = {"label": spec["label"], "type": spec["type"], "scope": spec["scope"], + "range": mrange, "desc": spec["desc"]} + if spec.get("reverse"): + metrics[key]["reverse"] = True + + present = set(metrics) + for r in residues: + r["metrics"] = {k: v for k, v in r["metrics"].items() if k in present} + for v in r["variants"]: + v["metrics"] = {k: val for k, val in v["metrics"].items() + if k in present and k in variant_metrics} + + data = { + "sample": args.sample, + "sequence": wt_seq, + "alignment": {"offset": off0, "identity": round(identity, 4), + "n_residues_structure": len(struct_residues)}, + "replicates": rep_ids, + "metrics": metrics, + "residues": residues, + } + + Path(args.out_json).write_text(json.dumps(data, indent=2)) + + threedmol_js = Path(args.threedmol).read_text() + template = Path(args.template).read_text() + html = (template + .replace("/*__THREEDMOL_JS__*/", threedmol_js) + .replace('"__PDB_TEXT__"', json.dumps(pdb_text)) + .replace("/*__DATA_JSON__*/", json.dumps(data)) + .replace("__SAMPLE__", args.sample)) + Path(args.out_html).write_text(html) + + Path("versions.yml").write_text( + '"${task.process}":\\n' + + " python: " + platform.python_version() + "\\n" + + " pandas: " + pd.__version__ + "\\n" + + " numpy: " + np.__version__ + "\\n" + ) + print(f"Wrote {args.out_html} (alignment offset={off0}, identity={identity:.2%}, " + f"{len(residues)} residues)") + + +if __name__ == "__main__": + main() diff --git a/modules/local/variantcounting/environment.yml b/modules/local/variantcounting/environment.yml new file mode 100644 index 0000000..9397a3f --- /dev/null +++ b/modules/local/variantcounting/environment.yml @@ -0,0 +1,14 @@ +channels: + - conda-forge + - bioconda +dependencies: + - conda-forge::python=3.12.13 + - bioconda::pysam=0.24.0 + - conda-forge::pyarrow=24.0.0 + - conda-forge::numpy=2.5.1 + - conda-forge::biopython=1.87 + # polars-lts-cpu (no AVX2 requirement) so the amd64 image also runs under + # emulation on Apple Silicon; the mutscan container is amd64-only. + - pip + - pip: + - polars-lts-cpu==1.33.1 diff --git a/modules/local/variantcounting/main.nf b/modules/local/variantcounting/main.nf new file mode 100644 index 0000000..5ee7eec --- /dev/null +++ b/modules/local/variantcounting/main.nf @@ -0,0 +1,40 @@ +process VARIANTCOUNTING { + tag "$meta.id" + label 'process_high' + + conda "${moduleDir}/environment.yml" + + container "${ workflow.containerEngine == 'singularity' + ? 'https://community-cr-prod.seqera.io/docker/registry/v2/blobs/sha256/a6/a66af652f7b1c6d9dab9f081f5676bd9452d653eed43034f20a2cf172921a4cf/data' + : 'community.wave.seqera.io/library/bioconductor-biostrings_bioconductor-mutscan_pysam_biopython_pruned:fb9a2095922ddd59' }" + + input: + tuple val(meta), path(bam), path(bai) + path fasta + val reading_frame + val min_counts + val base_qual + val min_flank + + output: + tuple val(meta), path("*.variant_counts.tsv"), emit: variant_counts + path "variant_counts_columns.tsv" , emit: columns + path "versions.yml" , emit: versions + + when: + task.ext.when == null || task.ext.when + + script: + template 'count_variants.py' + + stub: + """ + touch ${meta.id}.variant_counts.tsv + touch variant_counts_columns.tsv + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + variantcounting: stub + END_VERSIONS + """ +} diff --git a/modules/local/variantcounting/meta.yml b/modules/local/variantcounting/meta.yml new file mode 100644 index 0000000..5669b02 --- /dev/null +++ b/modules/local/variantcounting/meta.yml @@ -0,0 +1,74 @@ +name: "variantcounting" +description: Count single- and multi-nucleotide variants per read against a reference ORF, producing a GATK-AnalyzeSaturationMutagenesis-compatible variant count table. +keywords: + - deep mutational scanning + - variant calling + - saturation mutagenesis + - counting +tools: + - pysam: + description: Python interface for reading/manipulating SAM/BAM alignment files (htslib). + homepage: https://pysam.readthedocs.io + licence: ["MIT"] + - polars: + description: Fast DataFrame library. + homepage: https://pola.rs + licence: ["MIT"] + - biopython: + description: Tools for biological computation (sequence translation). + homepage: https://biopython.org + licence: ["BSD-3-Clause"] + +input: + - - meta: + type: map + description: Groovy Map containing sample information, e.g. `[ id:'sample1' ]` + - bam: + type: file + description: Coordinate-sorted, indexed BAM of premerged reads aligned to the reference. + pattern: "*.bam" + - bai: + type: file + description: BAM index (staged alongside the BAM). + pattern: "*.{bai,csi}" + - - fasta: + type: file + description: Reference FASTA containing the ORF. + pattern: "*.{fa,fasta}" + - - reading_frame: + type: string + description: ORF range as 1-based inclusive `start-stop` (GATK `--orf` convention), e.g. `352-1383`. + - - min_counts: + type: integer + description: Minimum number of observations for a variant to be reported (GATK `--min-variant-obs`). + - - base_qual: + type: integer + description: Minimum base quality for a mutation to be counted. + - - min_flank: + type: integer + description: Minimum distance (nt) of a mutation/covered base from a read edge; 0 disables edge trimming. + +output: + - variant_counts: + - meta: + type: map + description: Groovy Map containing sample information + - "*.variant_counts.tsv": + type: file + description: Tab-separated variant count table (no header), GATK-compatible 9 columns. + pattern: "*.variant_counts.tsv" + - columns: + - variant_counts_columns.tsv: + type: file + description: Concise explanation of each output column. + pattern: "variant_counts_columns.tsv" + - versions: + - versions.yml: + type: file + description: File containing software versions + pattern: "versions.yml" + +authors: + - "@BenjaminWehnert1008" +maintainers: + - "@BenjaminWehnert1008" diff --git a/modules/local/variantcounting/templates/count_variants.py b/modules/local/variantcounting/templates/count_variants.py new file mode 100644 index 0000000..73e460b --- /dev/null +++ b/modules/local/variantcounting/templates/count_variants.py @@ -0,0 +1,522 @@ +#!/usr/bin/env python + +# Nextflow template: values below are interpolated by Nextflow. The whole file is compiled +# as one Groovy GString, so every backslash must be doubled and no triple-quoted strings +# are allowed anywhere in this file, including comments. + +#-- import modules --# +import os +import sys +import re +import gc +import platform +import pysam +import numpy as np +import pyarrow as pa +import polars as pl +import Bio +from Bio import SeqIO +from Bio.Seq import Seq +from datetime import datetime +from concurrent.futures import ProcessPoolExecutor, as_completed +from collections import defaultdict + +#-- functions --# +def init_bam(bam_path): + global bam_file_cov + bam_file_cov = pysam.AlignmentFile(bam_path, "rb") + +def get_base_cov(chrom, start, end, qual, min_flank): + if min_flank and min_flank > 0: + cov = defaultdict(int) + for read in bam_file_cov.fetch(chrom, start, end): + if read.is_unmapped: + continue + quals = read.query_qualities + qlen = read.query_length + if quals is None or qlen is None: + continue + lo = min_flank + hi = qlen - min_flank + for qpos, refpos in read.get_aligned_pairs(matches_only=True): + if refpos is None or qpos is None: + continue + if refpos < start or refpos >= end: + continue + if qpos < lo or qpos >= hi: + continue + if quals[qpos] < qual: + continue + cov[refpos] += 1 + return dict(cov) + + A, C, G, T = bam_file_cov.count_coverage(chrom, start, end, quality_threshold = qual) + coverage_array = np.array(A, dtype=np.uint32) + \\ + np.array(C, dtype=np.uint32) + \\ + np.array(G, dtype=np.uint32) + \\ + np.array(T, dtype=np.uint32) + + # key on 0-based ref position to match ref_pos lookups in gatk_formating + return {start + pos: int(cov) for pos, cov in enumerate(coverage_array)} + +def chunk_ranges(start, end, sub_region_size): + for s in range(start, end, sub_region_size): + yield s, min(s + sub_region_size, end) + +def get_base_cov_in_chunk(bam_path, chrom, start, end, qual, sub_region_size, threads, min_flank): + chunks = list(chunk_ranges(start, end, sub_region_size)) + dicts = [] + + with ProcessPoolExecutor(max_workers = threads, initializer = init_bam, initargs = (bam_path,)) as executor: + futures = [ + executor.submit(get_base_cov, chrom, s, e, qual, min_flank) + for s, e in chunks + ] + + for f in as_completed(futures): + dicts.append(f.result()) + + merged_dict = {} + for d in dicts: + merged_dict.update(d) + + return merged_dict + +def init_worker(): + global global_dict_base_cov + global_dict_base_cov = dict_base_cov + +def extract_read_info(read): + read_seq = read.query_sequence + read_qual = read.query_qualities + + return { + 'ref': read.reference_name, + 'pos': read.reference_start, + 'seq': read_seq, + 'qual': read_qual, + 'md': read.get_tag('MD'), + 'cigar': read.cigartuples + } + +def parse_md(read): + read_pos = read.get('pos') + read_md = read.get('md') + md_pattern = re.finditer(r'(\\d+)|([A-Z]+)|(\\^[A-Z]+)', read_md) + + base_ref_pos = read_pos + + variants = [] + for match in md_pattern: + if match.group(1): + num_matches = int(match.group(1)) + base_ref_pos += num_matches + elif match.group(2): + for base in match.group(2): + variants.append(('X', base_ref_pos, base)) + base_ref_pos += 1 + elif match.group(3): + deleted_bases = match.group(3)[1:] + for base in deleted_bases: + variants.append(('D', base_ref_pos, base)) + base_ref_pos += 1 + + return variants + +def gatk_formating(variants): + if not variants: + return 0, 0, "", 0, "", "", "" + + varying_bases = 0 + varying_codons = 0 + base_mut = "" + codon_mut = "" + aa_mut = "" + pos_mut = "" + + base_pos = [ v['ref_pos'] for v in variants if v['var_type'] == 'X' ] + base_cov = [ global_dict_base_cov.get(pos, 0) for pos in base_pos ] + base_cov_avg = int(np.mean(base_cov)) if base_cov else 0 + + base_variants = [ v for v in variants if v['var_type'] == 'X' ] + varying_bases = len(base_variants) + + # base_mut positions are 1-based to match GATK AnalyzeSaturationMutagenesis + base_mut = ", ".join( + f"{v['ref_pos'] + 1}:{v['ref_base']}>{v['alt_base']}" + for v in base_variants + ) + + codon_changes = defaultdict(list) + for v in base_variants: + codon_idx = ((v['ref_pos'] - orf_start) // 3) + 1 + codon_changes[codon_idx].append(v) + + varying_codons = len(codon_changes) + + codon_mut_list = [] + aa_mut_list = [] + pos_mut_list = [] + + for codon_idx in sorted(codon_changes): + vars_in_codon = codon_changes[codon_idx] + ref_codon = str(codon_dict[chrom][codon_idx]) + codon_list = list(ref_codon) + + for v in vars_in_codon: + pos_in_codon = (v['ref_pos'] - orf_start) % 3 + codon_list[pos_in_codon] = v['alt_base'] + + alt_codon = "".join(codon_list) + codon_mut_list.append(f"{codon_idx}:{ref_codon}>{alt_codon}") + + ref_aa = str(Seq(ref_codon).translate()) + alt_aa = str(Seq(alt_codon).translate()) + + if alt_aa == ref_aa: + mut_type = "S" + elif alt_aa == "*": + mut_type = "N" + else: + mut_type = "M" + + # GATK writes the stop codon as X, not * + ref_aa_s = "X" if ref_aa == "*" else ref_aa + alt_aa_s = "X" if alt_aa == "*" else alt_aa + + aa_mut_list.append(f"{mut_type}:{ref_aa_s}>{alt_aa_s}") + pos_mut_list.append(f"{ref_aa_s}{codon_idx}{alt_aa_s}") + + codon_mut = ", ".join(codon_mut_list) + aa_mut = ", ".join(aa_mut_list) + pos_mut = ";".join(pos_mut_list) + + return base_cov_avg, varying_bases, base_mut, varying_codons, codon_mut, aa_mut, pos_mut + +def parse_read(read, orf_start, orf_end, base_qual, min_flank): + read_ref = read.get('ref') + read_pos = read.get('pos') + read_seq = read.get('seq') + read_qual = read.get('qual') + read_cigar = read.get('cigar') + read_md_variants = parse_md(read) + + if not read_md_variants: + return [] + + qlen = len(read_seq) + base_ref_pos = read_pos + base_read_pos = 0 + md_index = 0 + variants = [] + for op, op_len in read_cigar: + if op == 0: # M, match + for i in range(op_len): + if md_index < len(read_md_variants) and read_md_variants[md_index][1] == base_ref_pos: + var_type, _, ref_base = read_md_variants[md_index] + alt_base = read_seq[base_read_pos] + alt_qual = read_qual[base_read_pos] + alt_idx = (base_ref_pos - orf_start) % 3 + 1 + codon_idx = ((base_ref_pos - orf_start) // 3) + 1 + variants.append({ 'var_type': var_type, + 'ref_name': read_ref, + 'ref_pos': base_ref_pos, + 'ref_base': ref_base, + 'alt_base': alt_base, + 'alt_qual': alt_qual, + 'alt_idx' : alt_idx, + 'read_idx': base_read_pos, + 'codon_idx': codon_idx }) + md_index += 1 + base_ref_pos += 1 + base_read_pos += 1 + elif op == 1: # I, insertion (indel-containing reads are dropped upstream in bam_filter) + base_read_pos += op_len + elif op == 2: # D, deletion + for i in range(op_len): + if md_index < len(read_md_variants) and read_md_variants[md_index][1] == base_ref_pos: + var_type, _, ref_base = read_md_variants[md_index] + alt_idx = (base_ref_pos - orf_start) % 3 + 1 + codon_idx = ((base_ref_pos - orf_start) // 3) + 1 + variants.append({ 'var_type': 'D', + 'ref_name': read_ref, + 'ref_pos': base_ref_pos, + 'ref_base': ref_base, + 'alt_base': '-', + 'alt_qual': 99, + 'alt_idx' : alt_idx, + 'read_idx': base_read_pos, + 'codon_idx': codon_idx }) + md_index += 1 + base_ref_pos += 1 + elif op == 3: # N, skip + base_ref_pos += op_len + elif op == 4: # S, softclip + base_read_pos += op_len + elif op == 5: # H, hardclip + continue + + if not variants: + return [] + + variants_filtered = [] + for var in variants: + if var['ref_pos'] >= orf_start and var['ref_pos'] <= orf_end: + if var['alt_qual'] >= base_qual: + # read-edge (min-flanking-length) filter + if min_flank > 0: + read_idx = var['read_idx'] + if read_idx < min_flank or read_idx >= qlen - min_flank: + continue + codons_tmp = codon_dict[var['ref_name']] + ref_codon = codons_tmp[var['codon_idx']] + variants_filtered.append({ 'var_type': var['var_type'], + 'ref_name': var['ref_name'], + 'ref_pos': var['ref_pos'], + 'ref_base': var['ref_base'], + 'alt_base': var['alt_base'], + 'alt_qual': var['alt_qual'], + 'alt_idx' : var['alt_idx'], + 'codon_idx': var['codon_idx'], + 'ref_codon': ref_codon }) + + return gatk_formating(variants_filtered) + +def batch_parse_reads(batch_reads, orf_start, orf_end, base_qual, min_flank): + cols = { + "base_cov_avg": [], + "varying_bases": [], + "base_mut": [], + "varying_codons": [], + "codon_mut": [], + "aa_mut": [], + "pos_mut": [] + } + + for read in batch_reads: + result = parse_read(read, orf_start, orf_end, base_qual, min_flank) + + if not result or result[2] == "": + continue + + cols["base_cov_avg"].append(result[0]) + cols["varying_bases"].append(result[1]) + cols["base_mut"].append(result[2]) + cols["varying_codons"].append(result[3]) + cols["codon_mut"].append(result[4]) + cols["aa_mut"].append(result[5]) + cols["pos_mut"].append(result[6]) + + if not cols["base_mut"]: + return None + + return pa.table(cols) + +def function_for_processpool(args_tuple): + return batch_parse_reads(*args_tuple) + +def empty_yield_df(): + return pl.DataFrame([], schema={ + "base_cov_avg": pl.Int64, + "varying_bases": pl.Int64, + "base_mut": pl.Utf8, + "varying_codons": pl.Int64, + "codon_mut": pl.Utf8, + "aa_mut": pl.Utf8, + "pos_mut": pl.Utf8, + "counts": pl.Int64 + }, orient = "row") + +def build_yield_df(results): + if results: + df_yield = pl.from_arrow(pa.concat_tables(results)) + df_yield = df_yield.filter(pl.col("base_mut") != "") + df_yield = df_yield.with_columns(pl.lit(1).alias("counts")) + return df_yield + return empty_yield_df() + +def read_bam_in_chunk(bam_path, orf_start, orf_end, base_qual, min_flank, chunk_size, threads): + with pysam.AlignmentFile(bam_path, "rb", threads = threads) as bam_file, \\ + ProcessPoolExecutor(max_workers = threads, initializer = init_worker) as executor: + + batch_size = min(chunk_size, 5000) + + read_chunk = [] + for read in bam_file.fetch(until_eof=True): + if read.is_unmapped or not read.has_tag('MD'): + continue + + # perfectly-aligned (wildtype) reads carry no variants + if re.fullmatch(r'\\d+', read.get_tag('MD')): + continue + + read_chunk.append(extract_read_info(read)) + + if len(read_chunk) >= chunk_size: + read_batches = [ + read_chunk[i:i + batch_size] + for i in range(0, len(read_chunk), batch_size) + ] + + futures = [ + executor.submit(function_for_processpool, (batch, orf_start, orf_end, base_qual, min_flank)) + for batch in read_batches + ] + + results = [] + for f in as_completed(futures): + batch_result = f.result() + if batch_result: + results.append(batch_result) + + df_yield = build_yield_df(results) + read_chunk = [] + del read_batches, futures, results + gc.collect() + + yield df_yield + + if read_chunk: + read_batches = [ + read_chunk[i:i + batch_size] + for i in range(0, len(read_chunk), batch_size) + ] + + futures = [ + executor.submit(function_for_processpool, (batch, orf_start, orf_end, base_qual, min_flank)) + for batch in read_batches + ] + + results = [] + for f in as_completed(futures): + batch_result = f.result() + if batch_result: + results.append(batch_result) + + df_yield = build_yield_df(results) + del read_chunk, read_batches, futures, results + gc.collect() + + yield df_yield + +def write_column_sheet(path): + rows = [ + ("column", "description"), + ("counts", "Number of reads supporting this variant (observations)"), + ("cov", "Mean sequencing coverage across the variant's mutated base positions"), + ("mean_length_variant_reads", "Placeholder (0); mean read length of supporting reads is not computed"), + ("varying_bases", "Number of nucleotide substitutions in the variant"), + ("base_mut", "Nucleotide change(s), 1-based reference position(s), e.g. 352:G>A"), + ("varying_codons", "Number of codons altered by the variant"), + ("codon_mut", "Codon change(s), 1-based codon index, e.g. 1:GCT>ACT"), + ("aa_mut", "Amino-acid change per codon; type S=synonymous, M=missense, N=nonsense; stop=X, e.g. M:A>T"), + ("pos_mut", "Compact per-codon amino-acid change, e.g. A1T (stop written as X)"), + ] + with open(path, "w") as fh: + for name, desc in rows: + fh.write(name + "\\t" + desc + "\\n") + +#-- Nextflow-interpolated arguments --# +class args: + input_bam = "$bam" + reference = "$fasta" + orf_range = "$reading_frame" # 1-based inclusive (GATK --orf convention) + min_counts = int("$min_counts") + base_qual = int("$base_qual") + min_flank = int("$min_flank") + threads = max(1, int("$task.cpus")) + chunk_size = 1000 + out_file = "${meta.id}.variant_counts.tsv" + +#-- main execution --# +if __name__ == "__main__": + write_column_sheet("variant_counts_columns.tsv") + + # orf_range is 1-based inclusive (GATK --orf convention); convert to 0-based + orf_start_1, orf_end_1 = map(int, args.orf_range.split('-')) + orf_start = orf_start_1 - 1 + orf_end = orf_end_1 - 1 + + ref_dict = SeqIO.to_dict(SeqIO.parse(args.reference, "fasta")) + + orf_dict = {} + codon_dict = {} + + if os.path.exists(args.out_file): + os.remove(args.out_file) + + print(f"{datetime.now().strftime('%Y-%m-%d %H:%M:%S')} Get the reference codons, please wait...", flush = True) + for chrom, record in ref_dict.items(): + orf_seq = record.seq[orf_start : orf_end + 1] + orf_dict[chrom] = orf_seq + ref_codons = {} + for i in range(0, len(orf_seq) - 2, 3): + codon_idx = i // 3 + 1 # 1-based codon index + ref_codon = orf_seq[i:i + 3] + if len(ref_codon) == 3: + ref_codons[codon_idx] = ref_codon + codon_dict[chrom] = ref_codons + + del ref_dict + gc.collect() + + print(f"{datetime.now().strftime('%Y-%m-%d %H:%M:%S')} Get the base coverage, please wait...", flush = True) + sub_region_size = max(1, (orf_end - orf_start + 1) // args.threads) + dict_base_cov = get_base_cov_in_chunk(args.input_bam, chrom, orf_start, orf_end + 1, args.base_qual, sub_region_size, args.threads, args.min_flank) + + print(f"{datetime.now().strftime('%Y-%m-%d %H:%M:%S')} Extracting read information, please wait...", flush = True) + list_results = [] + for i, chunk_result in enumerate(read_bam_in_chunk(args.input_bam, orf_start, orf_end, args.base_qual, args.min_flank, args.chunk_size, args.threads)): + print(f"{datetime.now().strftime('%Y-%m-%d %H:%M:%S')} |--> Processed chunk {i+1}", flush = True) + list_results.append(chunk_result) + print(f"{datetime.now().strftime('%Y-%m-%d %H:%M:%S')} |--> Finished.", flush = True) + + print(f"{datetime.now().strftime('%Y-%m-%d %H:%M:%S')} Creating the variant matrix, please wait...", flush = True) + list_results_filtered = [df for df in list_results if df.height > 0] + + if list_results_filtered: + df_variants = pl.concat(list_results_filtered, how = "vertical") + df_variants = df_variants.filter(pl.col("base_mut") != "") + df_variants_counts = ( df_variants.group_by("base_mut") + .agg([pl.col("counts").sum().alias("counts"), + pl.all().exclude(["base_mut", "counts"]).first()]) ) + del list_results, list_results_filtered, df_variants + gc.collect() + else: + df_variants_counts = pl.DataFrame([], schema={ + "base_mut": pl.Utf8, + "counts": pl.Int64, + "base_cov_avg": pl.Int64, + "varying_bases": pl.Int64, + "varying_codons": pl.Int64, + "codon_mut": pl.Utf8, + "aa_mut": pl.Utf8, + "pos_mut": pl.Utf8 + }, orient = "row") + + # matches GATK --min-variant-obs + df_variants_counts = df_variants_counts.filter(pl.col("counts") >= args.min_counts) + + print(f"{datetime.now().strftime('%Y-%m-%d %H:%M:%S')} Creating the output file, please wait...", flush = True) + df_variants_counts = df_variants_counts.select([pl.col("counts"), + pl.col("base_cov_avg").alias("cov"), + pl.lit(0).alias("mean_length_variant_reads"), + pl.col("varying_bases"), + pl.col("base_mut"), + pl.col("varying_codons"), + pl.col("codon_mut"), + pl.col("aa_mut"), + pl.col("pos_mut")]) + # GATK variantCounts has no header line + df_variants_counts.write_csv(args.out_file, separator = "\\t", include_header = False) + print(f"{datetime.now().strftime('%Y-%m-%d %H:%M:%S')} Done.", flush = True) + + #-- versions --# + with open("versions.yml", "w") as vf: + vf.write('"${task.process}":\\n') + vf.write(" python: " + platform.python_version() + "\\n") + vf.write(" pysam: " + pysam.__version__ + "\\n") + vf.write(" polars: " + pl.__version__ + "\\n") + vf.write(" pyarrow: " + pa.__version__ + "\\n") + vf.write(" numpy: " + np.__version__ + "\\n") + vf.write(" biopython: " + Bio.__version__ + "\\n") diff --git a/modules/local/visualization/counts_heatmap/environment.yml b/modules/local/visualization/counts_heatmap/environment.yml new file mode 100644 index 0000000..197283a --- /dev/null +++ b/modules/local/visualization/counts_heatmap/environment.yml @@ -0,0 +1,15 @@ +channels: + - conda-forge + - bioconda +dependencies: + - bioconda::bioconductor-biostrings=2.78.0 + - conda-forge::r-base=4.5.1 + - conda-forge::r-biocmanager=1.30.27 + - conda-forge::r-dplyr=1.1.4 + - conda-forge::r-ggplot2=4.0.2 + - conda-forge::r-reshape2=1.4.4 + - conda-forge::r-scales=1.4.0 + - conda-forge::r-stringr=1.6.0 + - conda-forge::r-tidyr=1.3.1 + - conda-forge::r-tidyverse=2.0.0 + - conda-forge::r-zoo=1.8_15 diff --git a/modules/local/visualization/counts_heatmap/main.nf b/modules/local/visualization/counts_heatmap/main.nf new file mode 100644 index 0000000..4ef05dd --- /dev/null +++ b/modules/local/visualization/counts_heatmap/main.nf @@ -0,0 +1,31 @@ +process VISUALIZATION_COUNTS_HEATMAP { + tag "$meta.id" + label 'process_single' + + conda "${moduleDir}/environment.yml" + + container "${ workflow.containerEngine == 'singularity' + ? 'https://community-cr-prod.seqera.io/docker/registry/v2/blobs/sha256/73/73a72ec77725aeb67678a74228938fdd6827b669d01a8c96951b1a8ef96eeb0f/data' + : 'community.wave.seqera.io/library/bioconductor-biostrings_bioconductor-mutscan_r-base_r-biocmanager_pruned:c65036d76406f342' }" + + input: + tuple val(meta), path(variantCounts_for_heatmaps) + val min_counts + + output: + tuple val(meta), path("counts_heatmap.pdf"), emit: counts_heatmap + path "versions.yml", emit: versions + + when: + task.ext.when == null || task.ext.when + + script: + template 'counts_heatmap.R' + + stub: + """ + touch counts_heatmap.pdf + echo "VISUALIZATION_COUNTS_HEATMAP:" > versions.yml + echo " stub-version: 0.0.0" >> versions.yml + """ +} diff --git a/modules/local/visualization/counts_heatmap/meta.yml b/modules/local/visualization/counts_heatmap/meta.yml new file mode 100644 index 0000000..41ac5d7 --- /dev/null +++ b/modules/local/visualization/counts_heatmap/meta.yml @@ -0,0 +1,51 @@ +name: "visualization_counts_heatmap" +description: Generates a comprehensive heatmap of variant counts, visualizing the mutation landscape across the entire reference sequence. +keywords: + - deep mutational scanning + - dms + - visualization + - heatmap + - mutation landscape +tools: + - "mutscan": + description: "R package for analysis of deep mutational scanning data" + homepage: "https://bioconductor.org/packages/release/bioc/html/mutscan.html" + documentation: "https://bioconductor.org/packages/release/bioc/manuals/mutscan/man/mutscan.pdf" + tool_dev_url: "https://github.com/csoneson/mutscan" + doi: "10.1186/s12859-023-05187-y" + licence: ["GPL-3.0-or-later"] + +input: + - - meta: + type: map + description: | + Groovy Map containing sample information + e.g. `[ id:'test', single_end:false ]` + - variantCounts_for_heatmaps: + type: file + description: CSV file containing processed variant counts formatted for heatmap generation + pattern: "*.{csv}" + - - min_counts: + type: integer + description: Minimum count threshold used to filter variants before plotting + +output: + - counts_heatmap: + - meta: + type: map + description: | + Groovy Map containing sample information + e.g. `[ id:'test', single_end:false ]` + - counts_heatmap.pdf: + type: file + description: PDF heatmap visualizing mutation frequencies across the sequence positions + pattern: "counts_heatmap.pdf" + - versions: + - versions.yml: + type: file + description: File containing software versions + pattern: "versions.yml" + +authors: + - "@BenjaminWehnert1008" + - "@MaximilianStammnitz" diff --git a/modules/local/visualization/counts_heatmap/templates/counts_heatmap.R b/modules/local/visualization/counts_heatmap/templates/counts_heatmap.R new file mode 100644 index 0000000..44ecb8d --- /dev/null +++ b/modules/local/visualization/counts_heatmap/templates/counts_heatmap.R @@ -0,0 +1,187 @@ +#!/usr/bin/env Rscript + +# Input: prepared GATK data path, output_path, threshold (same as used for prepare_gatk_data_for_counts_per_cov_heatmap function) +# Output: counts_per_cov_heatmap.pdf + +library(dplyr) +library(ggplot2) + +counts_heatmap <- function(input_csv_path, threshold = 3, output_pdf_path, img_format = "pdf") { + + # Inner function to add padding to the last row, adding 21 amino acids per position + pad_heatmap_data_long <- function(heatmap_data_long, min_non_na_value, num_positions_per_row = 75) { + all_amino_acids <- c("G", "A", "V", "L", "M", "I", "F", + "Y", "W", "K", "R", "H", "D", "E", + "S", "T", "C", "N", "Q", "P", "*") + + max_position <- max(heatmap_data_long\$position) + num_missing_positions <- num_positions_per_row - (max_position %% num_positions_per_row) + + if (num_missing_positions < num_positions_per_row) { + new_positions <- (max_position + 1):(max_position + num_missing_positions) + + # Add all 21 amino acid variants for each new position + padding_data <- expand.grid( + mut_aa = all_amino_acids, # All possible amino acids + position = new_positions # New positions to be padded + ) + + # Set placeholder values for the added positions to the exact smallest non-NA value + padding_data\$total_counts <- min_non_na_value # Set to the smallest non-NA value + padding_data\$wt_aa <- "Y" # Set wild-type amino acid to 'Y' + padding_data\$wt_aa_pos <- paste0("Y", padding_data\$position) # Create wt_aa_pos with correct positions + padding_data\$row_group <- max(heatmap_data_long\$row_group) # Set row group to the current last group + + # Add the new padding rows to heatmap_data_long + heatmap_data_long <- dplyr::bind_rows(heatmap_data_long, padding_data) + } + + return(heatmap_data_long) + } + + # Load the CSV data + heatmap_data <- read.csv(input_csv_path) + + # Check if the necessary column exists in the data + if (!"total_counts" %in% colnames(heatmap_data)) { + stop("The column 'total_counts' is not found in the data.") + } + + # Create heatmap_data_long by selecting necessary columns + heatmap_data_long <- heatmap_data %>% + select(mut_aa, position, total_counts, wt_aa) # Use 'total_counts' + + # Find the smallest non-NA value in total_counts + min_non_na_value <- min(heatmap_data_long\$total_counts, na.rm = TRUE) + + # Group positions by rows (75 positions per row) and calculate row_group + heatmap_data_long <- heatmap_data_long %>% + mutate(row_group = ((position - 1) %/% 75) + 1) # Grouping positions into rows + + # Apply padding to add missing positions at the end of the last row, using the calculated min value + heatmap_data_long <- pad_heatmap_data_long(heatmap_data_long, min_non_na_value) + + # Convert positions to numeric, sort them, and create wt_aa_pos for the plot + heatmap_data_long <- heatmap_data_long %>% + mutate(position = as.numeric(position)) %>% # Ensure position is numeric + arrange(position) %>% # Sort by position + mutate(wt_aa_pos = factor(paste0(wt_aa, position), levels = unique(paste0(wt_aa, position)))) # Create sorted factor levels for wt_aa_pos + + # Add a column to identify synonymous mutations (where mut_aa == wt_aa) + heatmap_data_long <- heatmap_data_long %>% + mutate(synonymous = mut_aa == wt_aa) + + # Definiere die korrekte Reihenfolge der Aminosäuren + amino_acid_order <- rev(c("G", "A", "V", "L", "M", "I", "F", + "Y", "W", "K", "R", "H", "D", "E", + "S", "T", "C", "N", "Q", "P", "*")) + + heatmap_data_long <- heatmap_data_long %>% + mutate(mut_aa = factor(mut_aa, levels = amino_acid_order)) + + # Bearbeite heatmap_data_long und erstelle syn_positions gleichzeitig + syn_positions <- heatmap_data_long %>% + mutate(mut_aa = factor(mut_aa, levels = amino_acid_order), + # Berechne die x-Koordinate, die pro Gruppe immer von 1 bis 75 verläuft + x = as.numeric(factor(wt_aa_pos, levels = unique(wt_aa_pos))) - ((row_group - 1) * 75), + y = as.numeric(factor(mut_aa, levels = amino_acid_order))) %>% + filter(synonymous == TRUE) + + # Calculate the number of row groups and adjust plot height dynamically + num_row_groups <- max(heatmap_data_long\$row_group) + plot_height <- num_row_groups * 4 + + # Set the limits for the color scale, ignoring NA (negative values are now NA) + min_count <- min(heatmap_data_long\$total_counts, na.rm = TRUE) + max_count <- max(heatmap_data_long\$total_counts, na.rm = TRUE) + max_position <- max(heatmap_data\$position) + + # Create the heatmap plot with explicit handling for positions > max_position + heatmap_plot <- ggplot(heatmap_data_long, aes(x = wt_aa_pos, y = mut_aa, fill = total_counts)) + + scale_fill_gradientn(colours = c(alpha("blue", 0), "blue"), na.value = "grey35", trans = "log", # Apply log transformation to the scale + limits = c(min_count, max_count), + breaks = scales::trans_breaks("log10", function(x) 10^x), # Logarithmic scale breaks + labels = scales::trans_format("log10", scales::math_format(10^.x))) + + scale_x_discrete(labels = function(x) { + numeric_pos <- as.numeric(gsub("[^0-9]", "", x)) + ifelse(numeric_pos > max_position, " ", x) + }) + + geom_tile() + + + # Add diagonal lines for synonymous mutations using geom_segment + geom_segment(data = syn_positions[syn_positions\$position <= max_position, ], + aes(x = x - 0.485, xend = x + 0.485, + y = y - 0.485, yend = y + 0.485, color = synonymous), + size = 0.2) + + + # Manuelle Farbskala für die diagonalen Linien + scale_color_manual(values = c("TRUE" = "grey10"), labels = c("TRUE" = "")) + + + theme_minimal() + + labs(title = "Heatmap of Counts per Variant", x = "Wild-type Amino Acid", y = "Mutant Amino Acid", fill = "Counts", color = "Synonymous Mutation") + + theme(plot.title = element_text(size = 16, face = "bold"), + axis.text.x = element_text(angle = 90, hjust = 1, vjust = 0.5, size = 12), + axis.text.y = element_text(size = 12), # Larger y-axis labels + axis.title.x = element_text(size = 16), + axis.title.y = element_text(size = 16), + legend.title = element_text(size = 14), # Larger legend title + legend.text = element_text(size = 12), # Larger legend text + panel.spacing = unit(0.1, "lines"), # Adjust panel spacing + strip.text = element_blank(), # Remove row group labels (facet numbers) + strip.background = element_blank(), + panel.grid.major = element_blank(), # Remove major grid lines + panel.grid.minor = element_blank()) + # Remove minor grid lines + facet_wrap(~ row_group, scales = "free_x", ncol = 1) + # Group by 75 positions per row + theme(panel.spacing = unit(0.2, "lines")) + + heatmap_plot <- heatmap_plot + + geom_point(data = heatmap_data_long, aes(size = ""), colour = "black", alpha = 0) # Invisible points for legend + heatmap_plot <- heatmap_plot + + guides(size = guide_legend(paste("Dropout (Counts <", threshold, ")"), override.aes = list(shape = 15, size = 8, colour = "grey35", alpha = 1))) # Define Legend for Dropouts + + # Save the heatmap plot + if (img_format == "pdf") { + ggsave(output_pdf_path, plot = heatmap_plot, width = 16, height = plot_height, dpi = 150, device = cairo_pdf) + } else { + ggsave(output_pdf_path, plot = heatmap_plot, width = 16, height = plot_height, dpi = 150) + } + + if (file.exists(output_pdf_path)) { + print("Heatmap image successfully created!") + } else { + print("Error: Heatmap image was not created.") + } +} + +##### +# run function +##### +counts_heatmap( + input_csv_path = "$variantCounts_for_heatmaps", + threshold = $min_counts, + output_pdf_path = "counts_heatmap.pdf", + img_format = "pdf" +) + +#### +# create versions.yml +#### +r_version <- strsplit(version[['version.string']], ' ')[[1]][3] +dplyr_version <- as.character(packageVersion("dplyr")) +ggplot2_version <- as.character(packageVersion("ggplot2")) + +if (is.null(r_version)) r_version <- "unknown" +if (length(dplyr_version) == 0) dplyr_version <- "unknown" +if (length(ggplot2_version) == 0) ggplot2_version <- "unknown" + +f <- file("versions.yml", "w") +writeLines( + c( + '"${task.process}":', + paste(' r-base:', r_version), + paste(' r-dplyr:', dplyr_version), + paste(' r-ggplot2:', ggplot2_version) + ), + f +) +close(f) diff --git a/modules/local/visualization/counts_per_cov/environment.yml b/modules/local/visualization/counts_per_cov/environment.yml new file mode 100644 index 0000000..197283a --- /dev/null +++ b/modules/local/visualization/counts_per_cov/environment.yml @@ -0,0 +1,15 @@ +channels: + - conda-forge + - bioconda +dependencies: + - bioconda::bioconductor-biostrings=2.78.0 + - conda-forge::r-base=4.5.1 + - conda-forge::r-biocmanager=1.30.27 + - conda-forge::r-dplyr=1.1.4 + - conda-forge::r-ggplot2=4.0.2 + - conda-forge::r-reshape2=1.4.4 + - conda-forge::r-scales=1.4.0 + - conda-forge::r-stringr=1.6.0 + - conda-forge::r-tidyr=1.3.1 + - conda-forge::r-tidyverse=2.0.0 + - conda-forge::r-zoo=1.8_15 diff --git a/modules/local/visualization/counts_per_cov/main.nf b/modules/local/visualization/counts_per_cov/main.nf new file mode 100644 index 0000000..e19bd40 --- /dev/null +++ b/modules/local/visualization/counts_per_cov/main.nf @@ -0,0 +1,31 @@ +process VISUALIZATION_COUNTS_PER_COV { + tag "$meta.id" + label 'process_single' + + conda "${moduleDir}/environment.yml" + + container "${ workflow.containerEngine == 'singularity' + ? 'https://community-cr-prod.seqera.io/docker/registry/v2/blobs/sha256/73/73a72ec77725aeb67678a74228938fdd6827b669d01a8c96951b1a8ef96eeb0f/data' + : 'community.wave.seqera.io/library/bioconductor-biostrings_bioconductor-mutscan_r-base_r-biocmanager_pruned:c65036d76406f342' }" + + input: + tuple val(meta), path(variantCounts_for_heatmaps) + val min_counts + + output: + tuple val(meta), path("counts_per_cov_heatmap.pdf"), emit: counts_per_cov_heatmap + path "versions.yml", emit: versions + + when: + task.ext.when == null || task.ext.when + + script: + template 'counts_per_cov_heatmap.R' + + stub: + """ + touch counts_per_cov_heatmap.pdf + echo "VISUALIZATION_COUNTS_PER_COV:" > versions.yml + echo " stub-version: 0.0.0" >> versions.yml + """ +} diff --git a/modules/local/visualization/counts_per_cov/meta.yml b/modules/local/visualization/counts_per_cov/meta.yml new file mode 100644 index 0000000..354351c --- /dev/null +++ b/modules/local/visualization/counts_per_cov/meta.yml @@ -0,0 +1,49 @@ +name: "visualization_counts_per_cov" +description: "Generates a heatmap visualizing the distribution of variant counts per coverage depth in Deep Mutational Scanning data." +keywords: + - deep mutational scanning + - dms + - visualization + - heatmap + - coverage +tools: + - "r-base": + description: "R is a free software environment for statistical computing and graphics" + homepage: "https://www.r-project.org/" + documentation: "https://cran.r-project.org/manuals.html" + licence: ["GPL-2.0-or-later"] + +input: + - - meta: + type: map + description: | + Groovy Map containing sample information + e.g. `[ id:'test', single_end:false ]` + - variantCounts_for_heatmaps: + type: file + description: CSV file containing processed variant counts formatted for heatmap generation + pattern: "*.{csv}" + - - min_counts: + type: integer + description: Minimum count threshold used to filter variants for visualization + +output: + - counts_per_cov_heatmap: + - meta: + type: map + description: | + Groovy Map containing sample information + e.g. `[ id:'test', single_end:false ]` + - counts_per_cov_heatmap.pdf: + type: file + description: PDF heatmap showing variant counts distributed across coverage depths + pattern: "counts_per_cov_heatmap.pdf" + - versions: + - versions.yml: + type: file + description: File containing software versions + pattern: "versions.yml" + +authors: + - "@BenjaminWehnert1008" + - "@MaximilianStammnitz" diff --git a/modules/local/visualization/counts_per_cov/templates/counts_per_cov_heatmap.R b/modules/local/visualization/counts_per_cov/templates/counts_per_cov_heatmap.R new file mode 100644 index 0000000..77518b7 --- /dev/null +++ b/modules/local/visualization/counts_per_cov/templates/counts_per_cov_heatmap.R @@ -0,0 +1,187 @@ +#!/usr/bin/env Rscript + +# Input: prefiltered GATK data path, output_path, threshold (same as used for prepare_gatk_data_for_counts_per_cov_heatmap function) +# Output: counts_per_cov_heatmap.pdf + +library(dplyr) +library(ggplot2) + +counts_per_cov_heatmap <- function(input_csv_path, threshold = 3, output_pdf_path, img_format = "pdf") { + + # Inner function to add padding to the last row, adding 21 amino acids per position + pad_heatmap_data_long <- function(heatmap_data_long, min_non_na_value, num_positions_per_row = 75) { + all_amino_acids <- c("G", "A", "V", "L", "M", "I", "F", + "Y", "W", "K", "R", "H", "D", "E", + "S", "T", "C", "N", "Q", "P", "*") + + max_position <- max(heatmap_data_long\$position) + num_missing_positions <- num_positions_per_row - (max_position %% num_positions_per_row) + + if (num_missing_positions < num_positions_per_row) { + new_positions <- (max_position + 1):(max_position + num_missing_positions) + + # Add all 21 amino acid variants for each new position + padding_data <- expand.grid( + mut_aa = all_amino_acids, # All possible amino acids + position = new_positions # New positions to be padded + ) + + # Set placeholder values for the added positions to the exact smallest non-NA value + padding_data\$total_counts_per_cov <- min_non_na_value # Set to the smallest non-NA value + padding_data\$wt_aa <- "Y" # Set wild-type amino acid to 'Y' + padding_data\$wt_aa_pos <- paste0("Y", padding_data\$position) # Create wt_aa_pos with correct positions + padding_data\$row_group <- max(heatmap_data_long\$row_group) # Set row group to the current last group + + # Add the new padding rows to heatmap_data_long + heatmap_data_long <- dplyr::bind_rows(heatmap_data_long, padding_data) + } + + return(heatmap_data_long) + } + + # Load the CSV data + heatmap_data <- read.csv(input_csv_path) + + # Check if the necessary column exists in the data + if (!"total_counts_per_cov" %in% colnames(heatmap_data)) { + stop("The column 'total_counts_per_cov' is not found in the data.") + } + + # Create heatmap_data_long by selecting necessary columns + heatmap_data_long <- heatmap_data %>% + select(mut_aa, position, total_counts_per_cov, wt_aa) # Use 'total_counts_per_cov' + + # Find the smallest non-NA value in total_counts_per_cov + min_non_na_value <- min(heatmap_data_long\$total_counts_per_cov, na.rm = TRUE) + + # Group positions by rows (75 positions per row) and calculate row_group + heatmap_data_long <- heatmap_data_long %>% + mutate(row_group = ((position - 1) %/% 75) + 1) # Grouping positions into rows + + # Apply padding to add missing positions at the end of the last row, using the calculated min value + heatmap_data_long <- pad_heatmap_data_long(heatmap_data_long, min_non_na_value) + + # Convert positions to numeric, sort them, and create wt_aa_pos for the plot + heatmap_data_long <- heatmap_data_long %>% + mutate(position = as.numeric(position)) %>% # Ensure position is numeric + arrange(position) %>% # Sort by position + mutate(wt_aa_pos = factor(paste0(wt_aa, position), levels = unique(paste0(wt_aa, position)))) # Create sorted factor levels for wt_aa_pos + + # Add a column to identify synonymous mutations (where mut_aa == wt_aa) + heatmap_data_long <- heatmap_data_long %>% + mutate(synonymous = mut_aa == wt_aa) + + # Definiere die korrekte Reihenfolge der Aminosäuren + amino_acid_order <- rev(c("G", "A", "V", "L", "M", "I", "F", + "Y", "W", "K", "R", "H", "D", "E", + "S", "T", "C", "N", "Q", "P", "*")) + + heatmap_data_long <- heatmap_data_long %>% + mutate(mut_aa = factor(mut_aa, levels = amino_acid_order)) + + # Bearbeite heatmap_data_long und erstelle syn_positions gleichzeitig + syn_positions <- heatmap_data_long %>% + mutate(mut_aa = factor(mut_aa, levels = amino_acid_order), + # Berechne die x-Koordinate, die pro Gruppe immer von 1 bis 75 verläuft + x = as.numeric(factor(wt_aa_pos, levels = unique(wt_aa_pos))) - ((row_group - 1) * 75), + y = as.numeric(factor(mut_aa, levels = amino_acid_order))) %>% + filter(synonymous == TRUE) + + # Calculate the number of row groups and adjust plot height dynamically + num_row_groups <- max(heatmap_data_long\$row_group) + plot_height <- num_row_groups * 4 + + # Set the limits for the color scale, ignoring NA (negative values are now NA) + min_count <- min(heatmap_data_long\$total_counts_per_cov, na.rm = TRUE) + max_count <- max(heatmap_data_long\$total_counts_per_cov, na.rm = TRUE) + max_position <- max(heatmap_data\$position) + + # Create the heatmap plot with explicit handling for positions > max_position + heatmap_plot <- ggplot(heatmap_data_long, aes(x = wt_aa_pos, y = mut_aa, fill = total_counts_per_cov)) + + scale_fill_gradientn(colours = c(alpha("blue", 0), "blue"), na.value = "grey35", trans = "log", # Apply log transformation to the scale + limits = c(min_count, max_count), + breaks = scales::trans_breaks("log10", function(x) 10^x), # Logarithmic scale breaks + labels = scales::trans_format("log10", scales::math_format(10^.x))) + + scale_x_discrete(labels = function(x) { + numeric_pos <- as.numeric(gsub("[^0-9]", "", x)) + ifelse(numeric_pos > max_position, " ", x) + }) + + geom_tile() + + + # Add diagonal lines for synonymous mutations using geom_segment + geom_segment(data = syn_positions[syn_positions\$position <= max_position, ], + aes(x = x - 0.485, xend = x + 0.485, + y = y - 0.485, yend = y + 0.485, color = synonymous), + size = 0.2) + + + # Manuelle Farbskala für die diagonalen Linien + scale_color_manual(values = c("TRUE" = "grey10"), labels = c("TRUE" = "")) + + + theme_minimal() + + labs(title = "Heatmap of Counts per Coverage for Mutations", x = "Wild-type Amino Acid", y = "Mutant Amino Acid", fill = "Counts per \\n Coverage", color = "Synonymous Mutation") + + theme(plot.title = element_text(size = 16, face = "bold"), + axis.text.x = element_text(angle = 90, hjust = 1, vjust = 0.5, size = 12), + axis.text.y = element_text(size = 12), # Larger y-axis labels + axis.title.x = element_text(size = 16), + axis.title.y = element_text(size = 16), + legend.title = element_text(size = 14), # Larger legend title + legend.text = element_text(size = 12), # Larger legend text + panel.spacing = unit(0.1, "lines"), # Adjust panel spacing + strip.text = element_blank(), # Remove row group labels (facet numbers) + strip.background = element_blank(), + panel.grid.major = element_blank(), # Remove major grid lines + panel.grid.minor = element_blank()) + # Remove minor grid lines + facet_wrap(~ row_group, scales = "free_x", ncol = 1) + # Group by 75 positions per row + theme(panel.spacing = unit(0.2, "lines")) + + heatmap_plot <- heatmap_plot + + geom_point(data = heatmap_data_long, aes(size = ""), colour = "black", alpha = 0) # Invisible points for legend + heatmap_plot <- heatmap_plot + + guides(size = guide_legend(paste("Dropout (Counts <", threshold, ")"), override.aes = list(shape = 15, size = 8, colour = "grey35", alpha = 1))) # Define Legend for Dropouts + + # Save the heatmap plot + if (img_format == "pdf") { + ggsave(output_pdf_path, plot = heatmap_plot, width = 16, height = plot_height, dpi = 150, device = cairo_pdf) + } else { + ggsave(output_pdf_path, plot = heatmap_plot, width = 16, height = plot_height, dpi = 150) + } + + if (file.exists(output_pdf_path)) { + print("Heatmap image successfully created!") + } else { + print("Error: Heatmap image was not created.") + } +} + +##### +# run function +##### +counts_per_cov_heatmap( + input_csv_path = "$variantCounts_for_heatmaps", + threshold = $min_counts, + output_pdf_path = "counts_per_cov_heatmap.pdf", + img_format = "pdf" +) + +#### +# create versions.yml +#### +r_version <- strsplit(version[['version.string']], ' ')[[1]][3] +dplyr_version <- as.character(packageVersion("dplyr")) +ggplot2_version <- as.character(packageVersion("ggplot2")) + +if (is.null(r_version)) r_version <- "unknown" +if (length(dplyr_version) == 0) dplyr_version <- "unknown" +if (length(ggplot2_version) == 0) ggplot2_version <- "unknown" + +f <- file("versions.yml", "w") +writeLines( + c( + '"${task.process}":', + paste(' r-base:', r_version), + paste(' r-dplyr:', dplyr_version), + paste(' r-ggplot2:', ggplot2_version) + ), + f +) +close(f) diff --git a/modules/local/visualization/error_correction_report/environment.yml b/modules/local/visualization/error_correction_report/environment.yml new file mode 100644 index 0000000..a72f7ec --- /dev/null +++ b/modules/local/visualization/error_correction_report/environment.yml @@ -0,0 +1,7 @@ +channels: + - conda-forge + - bioconda +dependencies: + - conda-forge::python=3.12.3 + - conda-forge::pandas=2.2.1 + - conda-forge::numpy=1.26.4 diff --git a/modules/local/visualization/error_correction_report/main.nf b/modules/local/visualization/error_correction_report/main.nf new file mode 100644 index 0000000..6038ef7 --- /dev/null +++ b/modules/local/visualization/error_correction_report/main.nf @@ -0,0 +1,43 @@ +// Build the self-contained error-correction HTML report for the whole run. +// +// One report, not one per file: every sequencing file's numbers travel in the page and each panel +// recomputes in the browser from whichever files the reader selects, so a single-file view is still +// one click away while cross-replicate spread (the error bars) becomes possible at all. +process VISUALIZATION_ERROR_CORRECTION_REPORT { + tag "${sample}" + label 'process_single' + + conda "${moduleDir}/environment.yml" + + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container + ? 'https://depot.galaxyproject.org/singularity/pandas:2.2.1' + : 'quay.io/biocontainers/pandas:2.2.1' }" + + input: + val sample // run-level name, for the heading + val ids_b64 // base64(JSON) [file id, ...] aligned with `corrected` + path corrected, stageAs: 'c*/*' // per-file tables share a basename -> numbered dirs + val method + val false_doubles_method // 'mle' | 'eb' - names the estimator in the report heading + path template_html + + output: + path "error_correction_report.html", emit: report + path "versions.yml" , emit: versions + + when: + task.ext.when == null || task.ext.when + + script: + template 'build_ec_report.py' + + stub: + """ + touch error_correction_report.html + cat <<-END_VERSIONS > versions.yml + "${task.process}": + python: "0.0.0" + pandas: "0.0.0" + END_VERSIONS + """ +} diff --git a/modules/local/visualization/error_correction_report/meta.yml b/modules/local/visualization/error_correction_report/meta.yml new file mode 100644 index 0000000..9cb45dc --- /dev/null +++ b/modules/local/visualization/error_correction_report/meta.yml @@ -0,0 +1,53 @@ +name: "visualization_error_correction_report" +description: Build a self-contained, per-sample HTML report visualising the effect of the sequencing-error correction (positional error bias, correction magnitude, and per-variant raw-vs-corrected counts). +keywords: + - deep mutational scanning + - error correction + - report + - visualisation +tools: + - "pandas": + description: "Fast, powerful data structures for data analysis in Python." + homepage: "https://pandas.pydata.org/" + licence: ["BSD-3-Clause"] + +input: + - - meta: + type: map + description: | + Groovy Map containing sample information + e.g. `[ id:'test', sample:'ORF1' ]` + - corrected: + type: file + description: Error-corrected, library-filtered counts (with `counts` corrected and `counts_raw` preserved). + pattern: "*.csv" + - - method: + type: string + description: Error-correction method label (e.g. `false_doubles`, `wildtype`). + - - false_doubles_method: + type: string + description: False-doubles estimator actually used (`mle` or `eb`); named in the report heading. + - - template_html: + type: file + description: HTML template into which the report data is injected. + pattern: "*.html" + +output: + - report: + - meta: + type: map + description: Groovy Map containing sample information + - "*_error_correction_report.html": + type: file + description: Self-contained error-correction report. + pattern: "*_error_correction_report.html" + - versions: + - versions.yml: + type: file + description: File containing software versions + pattern: "versions.yml" + +authors: + - "@BenjaminWehnert1008" +maintainers: + - "@BenjaminWehnert1008" diff --git a/modules/local/visualization/error_correction_report/templates/build_ec_report.py b/modules/local/visualization/error_correction_report/templates/build_ec_report.py new file mode 100644 index 0000000..e6719a9 --- /dev/null +++ b/modules/local/visualization/error_correction_report/templates/build_ec_report.py @@ -0,0 +1,158 @@ +#!/usr/bin/env python + +# Build the self-contained error-correction HTML report for a whole run. +# +# One report covers every sequencing file: the page carries each file's numbers separately and +# recomputes every panel in the browser from whichever files are selected, so deselecting down to a +# single file reproduces exactly what a per-file report used to show. +# +# Nextflow template: values below are interpolated; backslashes meant for Python are doubled. + +import base64 +import json +import platform +import re + +import numpy as np +import pandas as pd + +sample = "$sample" +method = "$method" +# The false-doubles estimator actually used (mle|eb), so the report can name it. Empty/other for the +# wildtype method, where it does not apply. +estimator = "$false_doubles_method" +# Each entry is {id, type, replicate} - type/replicate come straight from the samplesheet (general +# for any protein, not hard-coded here) and drive the input-vs-output split in the report. +files = json.loads(base64.b64decode("$ids_b64").decode("utf-8")) +paths = "$corrected".split() + +if len(files) != len(paths): + raise SystemExit(f"{len(files)} ids but {len(paths)} files staged") + +MUT = re.compile(r"^([A-Za-z*]+)(\\d+)([A-Za-z*]+)") + +# `base_mut` holds the nucleotide change(s) of a variant as ":>", comma-separated when +# the codon carries 2-3 changes (the NNK case). Positions are reference coordinates. +SNV = re.compile(r"^(\\d+):([ACGTacgt])>([ACGTacgt])\$") + +COMPLEMENT = {"A": "T", "C": "G", "G": "C", "T": "A"} + +# The six strand-symmetric substitution classes, pyrimidine-centric: a purine reference is +# reverse-complemented, so C>A and G>T are one and the same event. Order is canonical (as used for +# mutational signatures) and fixed - the colour of a class never depends on the data. +SNV_CLASSES = ["C>A", "C>G", "C>T", "T>A", "T>C", "T>G"] +SNV_LABELS = { + "C>A": "C>A / G>T", "C>G": "C>G / G>C", "C>T": "C>T / G>A", + "T>A": "T>A / A>T", "T>C": "T>C / A>G", "T>G": "T>G / A>C", +} + + +def parse_pos(s): + m = MUT.match(str(s)) + return (m.group(1), int(m.group(2)), m.group(3)) if m else (None, None, None) + + +def parse_snvs(base_mut): + """"1220:T>G, 1221:T>G" -> [(1220,'T','G'), (1221,'T','G')]""" + out = [] + for part in str(base_mut).split(","): + m = SNV.match(part.strip()) + if m: + out.append((int(m.group(1)), m.group(2).upper(), m.group(3).upper())) + return out + + +def classify_snv(ref, alt): + """Collapse a substitution onto its pyrimidine-centric class key.""" + if ref not in COMPLEMENT or alt not in COMPLEMENT or ref == alt: + return None + if ref in ("A", "G"): + ref, alt = COMPLEMENT[ref], COMPLEMENT[alt] + key = ref + ">" + alt + return key if key in SNV_LABELS else None + + +def numcol(df, name): + return (pd.to_numeric(df[name], errors="coerce") if name in df.columns + else pd.Series([np.nan] * len(df))) + + +def f(x, nd): + return None if x is None or x != x else round(float(x), nd) + + +# Variants are keyed on `codon_mut`, not `pos_mut`. `pos_mut` is NOT unique - one amino-acid +# substitution is reachable through up to three codons (6898 unique pos_mut across 10254 rows on the +# GID1A run), so keying on it would silently merge distinct nucleotide variants. `codon_mut` is +# unique (10254/10254). +# +# The files do not share a variant set either (union 10771 vs intersection 6886 on GID1A), so this is +# a union: a variant absent from a file gets None there, never 0. Absent means "not observed above +# threshold in that file", which is missing data, not a measured zero - averaging it in as 0 would +# drag every frequency down. The browser skips Nones and reports how many files contributed. +variants = {} +n_files = len(files) + +for fi, (fid, path) in enumerate(zip(files, paths)): + df = pd.read_csv(path) + raw = numcol(df, "counts_raw") + cor = numcol(df, "counts") + cpc_raw = numcol(df, "counts_per_cov_raw") + cpc_cor = numcol(df, "counts_per_cov") + posmut = df["pos_mut"].astype(str) + basemut = df["base_mut"].astype(str) if "base_mut" in df.columns else pd.Series([""] * len(df)) + codonmut = df["codon_mut"].astype(str) if "codon_mut" in df.columns else posmut + + for i in range(len(df)): + r = raw.iloc[i] + if pd.isna(r): + continue + key = codonmut.iloc[i].strip() + v = variants.get(key) + if v is None: + wt, pos, _mut = parse_pos(posmut.iloc[i]) + nt = basemut.iloc[i].strip() + snvs = parse_snvs(nt) + # A class is only meaningful when a single nucleotide changed; NNK codon variants + # (2-3 changes) mix classes and stay unclassified by design. + kls = classify_snv(snvs[0][1], snvs[0][2]) if len(snvs) == 1 else None + v = variants[key] = { + "key": key, "pos_mut": posmut.iloc[i], "pos": pos, "wt_aa": wt, + "nt": nt, "cls": kls, + "ntpos": (snvs[0][0] if len(snvs) == 1 else None), + "raw": [None] * n_files, "cor": [None] * n_files, + "f_raw": [None] * n_files, "f_cor": [None] * n_files, + } + c = cor.iloc[i] + v["raw"][fi] = f(r, 3) + v["cor"][fi] = 0.0 if pd.isna(c) else f(c, 3) + v["f_raw"][fi] = f(cpc_raw.iloc[i], 10) + v["f_cor"][fi] = 0.0 if pd.isna(cpc_cor.iloc[i]) else f(cpc_cor.iloc[i], 10) + +data = { + "sample": sample, + "method": method, + "estimator": estimator, + "files": files, + "classes": [{"cls": k, "label": SNV_LABELS[k]} for k in SNV_CLASSES], + "variants": sorted(variants.values(), key=lambda v: (v["pos"] if v["pos"] is not None else 0, v["key"])), +} + +data_json = json.dumps(data, separators=(",", ":")).replace(" versions.yml + echo " stub-version: 0.0.0" >> versions.yml + """ +} diff --git a/modules/local/visualization/global_pos_biases_counts/meta.yml b/modules/local/visualization/global_pos_biases_counts/meta.yml new file mode 100644 index 0000000..8a02b08 --- /dev/null +++ b/modules/local/visualization/global_pos_biases_counts/meta.yml @@ -0,0 +1,61 @@ +name: "visualization_global_pos_biases_counts" +description: Calculates and visualizes the rolling mean of variant counts and counts normalized by coverage across the reference sequence to identify regional biases or mutation hotspots. +keywords: + - deep mutational scanning + - dms + - visualization + - rolling mean + - sliding window + - bias +tools: + - "mutscan": + description: "R package for analysis of deep mutational scanning data" + homepage: "https://bioconductor.org/packages/release/bioc/html/mutscan.html" + documentation: "https://bioconductor.org/packages/release/bioc/manuals/mutscan/man/mutscan.pdf" + tool_dev_url: "https://github.com/csoneson/mutscan" + doi: "10.1186/s12859-023-05187-y" + licence: ["GPL-3.0-or-later"] + +input: + - - meta: + type: map + description: | + Groovy Map containing sample information + e.g. `[ id:'test', single_end:false ]` + - variantCounts_filtered_by_library: + type: file + description: CSV file containing variant counts filtered against the target library + pattern: "*.{csv}" + - - aa_seq: + type: file + description: Text or FASTA file containing the reference amino acid sequence + - - sliding_window_size: + type: integer + description: The size of the window (number of amino acids) used for calculating the rolling mean + +output: + - rolling_counts: + - meta: + type: map + description: Groovy Map containing sample information + - rolling_counts.pdf: + type: file + description: PDF plot showing the rolling mean of raw variant counts across positions + pattern: "rolling_counts.pdf" + - rolling_counts_per_cov: + - meta: + type: map + description: Groovy Map containing sample information + - rolling_counts_per_cov.pdf: + type: file + description: PDF plot showing the rolling mean of counts normalized by local coverage + pattern: "rolling_counts_per_cov.pdf" + - versions: + - versions.yml: + type: file + description: File containing software versions + pattern: "versions.yml" + +authors: + - "@BenjaminWehnert1008" + - "@MaximilianStammnitz" diff --git a/modules/local/visualization/global_pos_biases_counts/templates/global_position_biases_counts_and_counts_per_cov.R b/modules/local/visualization/global_pos_biases_counts/templates/global_position_biases_counts_and_counts_per_cov.R new file mode 100644 index 0000000..2ec9d91 --- /dev/null +++ b/modules/local/visualization/global_pos_biases_counts/templates/global_position_biases_counts_and_counts_per_cov.R @@ -0,0 +1,186 @@ +#!/usr/bin/env Rscript + +# input: prefiltered gatk path, aa seq path, window_size (sliding window), output_path_folder +# output: two lineplots showing counts & counts per coverage divided in types of variants (single-/double-/triple-base exchange) + +library(zoo) # sliding window +library(dplyr) +library(ggplot2) +library(scales) + +position_biases <- function(prefiltered_gatk_path, aa_seq_path, window_size = 10) { + + # Load and process the data + prefiltered_gatk <- read.table(prefiltered_gatk_path, sep = ",", fill = NA, header = TRUE) + prefiltered_gatk\$pos <- as.numeric(sub("(\\\\D)(\\\\d+)(\\\\D)", "\\\\2", prefiltered_gatk\$pos_mut)) + unique_pos <- unique(as.numeric(prefiltered_gatk\$pos)) + aa_seq <- readLines(aa_seq_path, warn = FALSE) + aa_seq_length <- nchar(aa_seq) + aa_positions <- seq(nchar(aa_seq)) + + means_counts_single <- rep(NA, nchar(aa_seq)) + means_counts_double <- rep(NA, nchar(aa_seq)) + means_counts_triple <- rep(NA, nchar(aa_seq)) + means_counts_per_cov_single <- rep(NA, nchar(aa_seq)) + means_counts_per_cov_double <- rep(NA, nchar(aa_seq)) + means_counts_per_cov_triple <- rep(NA, nchar(aa_seq)) + + + # Loop through each position in the amino acid sequence + for (i in 1:(nchar(aa_seq))) { + + # Filter the data for the current position in aa_positions + window_data <- prefiltered_gatk %>% filter(prefiltered_gatk\$pos %in% aa_positions[i]) + + # Calculate mean for Single mutations (where varying_bases == 1) + window_data_single <- window_data %>% filter(varying_bases == 1) + means_counts_single[i] <- mean(window_data_single\$counts, na.rm = FALSE) + means_counts_per_cov_single[i] <- mean(window_data_single\$counts_per_cov, na.rm = FALSE) + + # Calculate mean for Double mutations (where varying_bases == 2) + window_data_double <- window_data %>% filter(varying_bases == 2) + means_counts_double[i] <- mean(window_data_double\$counts, na.rm = FALSE) + means_counts_per_cov_double[i] <- mean(window_data_double\$counts_per_cov, na.rm = FALSE) + + # Calculate mean for Triple mutations (where varying_bases == 3) + window_data_triple <- window_data %>% filter(varying_bases == 3) + means_counts_triple[i] <- mean(window_data_triple\$counts, na.rm = FALSE) + means_counts_per_cov_triple[i] <- mean(window_data_triple\$counts_per_cov, na.rm = FALSE) + } + + pos_bias_df <- data.frame(pos = seq(nchar(aa_seq))) + + + pos_bias_df\$rolling_mean_counts_single <- rollapply(means_counts_single, width = window_size, + FUN = function(x) (mean(x, na.rm = TRUE) + 0.00001), fill = "extend") + pos_bias_df\$rolling_mean_counts_double <- rollapply(means_counts_double, width = window_size, + FUN = function(x) (mean(x, na.rm = TRUE) + 0.00001), fill = "extend") + pos_bias_df\$rolling_mean_counts_triple <- rollapply(means_counts_triple, width = window_size, + FUN = function(x) (mean(x, na.rm = TRUE) + 0.00001), fill = "extend") + + pos_bias_df\$rolling_mean_counts_per_cov_single <- rollapply(means_counts_per_cov_single, width = window_size, + FUN = function(x) (mean(x, na.rm = TRUE) + 0.00001), fill = "extend") + pos_bias_df\$rolling_mean_counts_per_cov_double <- rollapply(means_counts_per_cov_double, width = window_size, + FUN = function(x) (mean(x, na.rm = TRUE) + 0.00001), fill = "extend") + pos_bias_df\$rolling_mean_counts_per_cov_triple <- rollapply(means_counts_per_cov_triple, width = window_size, + FUN = function(x) (mean(x, na.rm = TRUE) + 0.00001), fill = "extend") + + + # Replace NAs with 0 for rolling means and SE + pos_bias_df\$rolling_mean_counts_single[is.na(pos_bias_df\$rolling_mean_counts_single)] <- 0.00001 + pos_bias_df\$rolling_mean_counts_double[is.na(pos_bias_df\$rolling_mean_counts_double)] <- 0.00001 + pos_bias_df\$rolling_mean_counts_triple[is.na(pos_bias_df\$rolling_mean_counts_triple)] <- 0.00001 + pos_bias_df\$rolling_mean_counts_per_cov_single[is.na(pos_bias_df\$rolling_mean_counts_per_cov_single)] <- 0.000001 + pos_bias_df\$rolling_mean_counts_per_cov_double[is.na(pos_bias_df\$rolling_mean_counts_per_cov_double)] <- 0.000001 + pos_bias_df\$rolling_mean_counts_per_cov_triple[is.na(pos_bias_df\$rolling_mean_counts_per_cov_triple)] <- 0.000001 + + + + plots_theme <- list( + + # Add the minimal theme to the list + theme_minimal(), + + # Customize legend title and appearance + theme(legend.title = element_text(size = 10, face = "bold"), + legend.position = "right"), + + # Customize guides for the legend elements + guides( + color = guide_legend(title = "Mutation Type", order = 1), # Title for line color + fill = guide_legend(title = "Standard Deviation", order = 2), # Title for ribbon fill + linetype = guide_legend(title = "Required Coverage", order = 3) # Title for line type + ) + ) + + + # Placeholder name for the linetype + linetype_placeholder <- "Required coverage" + + rolling_counts_plot <- ggplot(pos_bias_df, aes(x = pos)) + + + # Add line for rolling mean coverage + geom_line(aes(y = rolling_mean_counts_single, color = "One Varying Base")) + + geom_line(aes(y = rolling_mean_counts_double, color = "Two Varying Bases")) + + geom_line(aes(y = rolling_mean_counts_triple, color = "Three Varying Bases")) + + + # Individual axis labels for this plot + xlab("Amino Acid Position") + + ylab("Counts") + + + # Manually set color and fill labels + scale_color_manual(name = "Mutation Type", + values = c("One Varying Base" = "chocolate", "Two Varying Bases" = "darkolivegreen3", "Three Varying Bases" = "deepskyblue1"), + limits = c("One Varying Base", "Two Varying Bases", "Three Varying Bases")) + # Color for line + + # Apply the saved theme and design elements at the end + plots_theme + + + scale_y_continuous(trans = 'log10', labels = scales::comma) + + + + + rolling_counts_per_cov_plot <- ggplot(pos_bias_df, aes(x = pos)) + + + # Add line for rolling mean coverage + geom_line(aes(y = rolling_mean_counts_per_cov_single, color = "One Varying Base")) + + geom_line(aes(y = rolling_mean_counts_per_cov_double, color = "Two Varying Bases")) + + geom_line(aes(y = rolling_mean_counts_per_cov_triple, color = "Three Varying Bases")) + + + # Individual axis labels for this plot + xlab("Amino Acid Position") + + ylab("Counts") + + + # Manually set color and fill labels + scale_color_manual(name = "Mutation Type", + values = c("One Varying Base" = "chocolate", "Two Varying Bases" = "darkolivegreen3", "Three Varying Bases" = "deepskyblue1"), + limits = c("One Varying Base", "Two Varying Bases", "Three Varying Bases")) + # Color and legend order for lines + + # Apply the saved theme and design elements at the end + plots_theme + + + scale_y_continuous(trans = 'log10', labels = scales::comma) + + + ggsave(filename = "rolling_counts.pdf", plot = rolling_counts_plot, device = "pdf", width = 10, height = 6) + ggsave(filename = "rolling_counts_per_cov.pdf", plot = rolling_counts_per_cov_plot, device = "pdf", width = 10, height = 6) +} + +##### +# run pipeline +##### +position_biases( + prefiltered_gatk_path = "$variantCounts_filtered_by_library", + aa_seq_path = "$aa_seq", + window_size = $sliding_window_size +) + +#### +# create versions.yml +#### +r_version <- strsplit(version[['version.string']], ' ')[[1]][3] +dplyr_version <- as.character(packageVersion("dplyr")) +ggplot2_version <- as.character(packageVersion("ggplot2")) +scales_version <- as.character(packageVersion("scales")) +zoo_version <- as.character(packageVersion("zoo")) + +if (is.null(r_version)) r_version <- "unknown" +if (length(dplyr_version) == 0) dplyr_version <- "unknown" +if (length(ggplot2_version) == 0) ggplot2_version <- "unknown" +if (length(scales_version) == 0) scales_version <- "unknown" +if (length(zoo_version) == 0) zoo_version <- "unknown" + +f <- file("versions.yml", "w") +writeLines( + c( + '"${task.process}":', + paste(' r-base:', r_version), + paste(' r-dplyr:', dplyr_version), + paste(' r-ggplot2:', ggplot2_version), + paste(' r-scales:', scales_version), + paste(' r-zoo:', zoo_version) + ), + f +) +close(f) diff --git a/modules/local/visualization/global_pos_biases_cov/environment.yml b/modules/local/visualization/global_pos_biases_cov/environment.yml new file mode 100644 index 0000000..197283a --- /dev/null +++ b/modules/local/visualization/global_pos_biases_cov/environment.yml @@ -0,0 +1,15 @@ +channels: + - conda-forge + - bioconda +dependencies: + - bioconda::bioconductor-biostrings=2.78.0 + - conda-forge::r-base=4.5.1 + - conda-forge::r-biocmanager=1.30.27 + - conda-forge::r-dplyr=1.1.4 + - conda-forge::r-ggplot2=4.0.2 + - conda-forge::r-reshape2=1.4.4 + - conda-forge::r-scales=1.4.0 + - conda-forge::r-stringr=1.6.0 + - conda-forge::r-tidyr=1.3.1 + - conda-forge::r-tidyverse=2.0.0 + - conda-forge::r-zoo=1.8_15 diff --git a/modules/local/visualization/global_pos_biases_cov/main.nf b/modules/local/visualization/global_pos_biases_cov/main.nf new file mode 100644 index 0000000..b6b18e3 --- /dev/null +++ b/modules/local/visualization/global_pos_biases_cov/main.nf @@ -0,0 +1,33 @@ +process VISUALIZATION_GLOBAL_POS_BIASES_COV { + tag "$meta.id" + label 'process_single' + + conda "${moduleDir}/environment.yml" + + container "${ workflow.containerEngine == 'singularity' + ? 'https://community-cr-prod.seqera.io/docker/registry/v2/blobs/sha256/73/73a72ec77725aeb67678a74228938fdd6827b669d01a8c96951b1a8ef96eeb0f/data' + : 'community.wave.seqera.io/library/bioconductor-biostrings_bioconductor-mutscan_r-base_r-biocmanager_pruned:c65036d76406f342' }" + + input: + tuple val(meta), path(variantCounts_filtered_by_library) + path aa_seq + val sliding_window_size + val aimed_cov + + output: + tuple val(meta), path("rolling_coverage.pdf"), emit: rolling_coverage + path "versions.yml", emit: versions + + when: + task.ext.when == null || task.ext.when + + script: + template 'global_position_biases_cov.R' + + stub: + """ + touch rolling_coverage.pdf + echo "VISUALIZATION_COUNTS_HEATMAP:" > versions.yml + echo " stub-version: 0.0.0" >> versions.yml + """ +} diff --git a/modules/local/visualization/global_pos_biases_cov/meta.yml b/modules/local/visualization/global_pos_biases_cov/meta.yml new file mode 100644 index 0000000..c0b6216 --- /dev/null +++ b/modules/local/visualization/global_pos_biases_cov/meta.yml @@ -0,0 +1,56 @@ +name: "visualization_global_pos_biases_cov" +description: Calculates and plots the rolling mean coverage across the protein sequence to identify positional biases, regions of low coverage, or potential experimental dropouts. +keywords: + - deep mutational scanning + - dms + - visualization + - coverage + - rolling window + - bias +tools: + - "mutscan": + description: "R package for analysis of deep mutational scanning data" + homepage: "https://bioconductor.org/packages/release/bioc/html/mutscan.html" + documentation: "https://bioconductor.org/packages/release/bioc/manuals/mutscan/man/mutscan.pdf" + tool_dev_url: "https://github.com/csoneson/mutscan" + doi: "10.1186/s12859-023-05187-y" + licence: ["GPL-3.0-or-later"] + +input: + - - meta: + type: map + description: | + Groovy Map containing sample information + e.g. `[ id:'test', single_end:false ]` + - variantCounts_filtered_by_library: + type: file + description: CSV file containing variant counts filtered against the target library + pattern: "*.{csv}" + - - aa_seq: + type: file + description: Text file containing the reference amino acid sequence + - - sliding_window_size: + type: integer + description: The size of the window (in amino acids) used for calculating the rolling mean coverage + - - aimed_cov: + type: integer + description: The target coverage threshold to visualize as a horizontal reference line in the plot + +output: + - rolling_coverage: + - meta: + type: map + description: Groovy Map containing sample information + - rolling_coverage.pdf: + type: file + description: PDF plot showing the rolling mean coverage across sequence positions + pattern: "rolling_coverage.pdf" + - versions: + - versions.yml: + type: file + description: File containing software versions + pattern: "versions.yml" + +authors: + - "@BenjaminWehnert1008" + - "@MaximilianStammnitz" diff --git a/modules/local/visualization/global_pos_biases_cov/templates/global_position_biases_cov.R b/modules/local/visualization/global_pos_biases_cov/templates/global_position_biases_cov.R new file mode 100644 index 0000000..0a2fda1 --- /dev/null +++ b/modules/local/visualization/global_pos_biases_cov/templates/global_position_biases_cov.R @@ -0,0 +1,121 @@ +#!/usr/bin/env Rscript + +# input: prefiltered gatk path, aa seq path, window_size (sliding window), output_path_folder, aimed counts per aa variant +# output: lineplot showing coverage over positions and dotted line: Assuming all potential 21 variants are equally distributed, this is the coverage one need to get the aimed counts per variant. + +library(zoo) # sliding window +library(dplyr) +library(ggplot2) +library(scales) +position_biases <- function(prefiltered_gatk_path, aa_seq_path, window_size = 10, output_file_path, targeted_counts_per_aa_variant = 100) { + + # Load and process the data + prefiltered_gatk <- read.table(prefiltered_gatk_path, sep = ",", fill = NA, header = TRUE) + prefiltered_gatk\$pos <- as.numeric(sub("(\\\\D)(\\\\d+)(\\\\D)", "\\\\2", prefiltered_gatk\$pos_mut)) + unique_pos <- unique(as.numeric(prefiltered_gatk\$pos)) + aa_seq <- readLines(aa_seq_path, warn = FALSE) + aa_seq_length <- nchar(aa_seq) + aa_positions <- seq(nchar(aa_seq)) + + means_cov <- rep(NA, nchar(aa_seq)) + + # Calculate means for cov (should be the same over all variants in one position) + for (i in 1:(nchar(aa_seq))) { + window_data <- prefiltered_gatk %>% filter(prefiltered_gatk\$pos %in% aa_positions[i]) + means_cov[i] <- mean(window_data\$cov, na.rm = FALSE) + } + + pos_bias_df <- data.frame(pos = seq(nchar(aa_seq))) + + # Log-transform the rolling means to avoid issues with zeros (log(y + 0.001)) + pos_bias_df\$rolling_mean_cov <- rollapply(means_cov, width = window_size, + FUN = function(x) (mean(x, na.rm = TRUE)), fill = "extend") + + # Generate the plot + plots_theme <- list( + + # Customize legend appearance (leave titles blank) + guides( + color = guide_legend(title = NULL, order = 1), # Title for line color + fill = guide_legend(title = NULL, order = 2), # Title for ribbon fill + linetype = guide_legend(title = NULL, order = 3) # Title for line type + ), + + # Apply minimal theme + theme_minimal() + ) + + + linetype_placeholder <- "Required coverage" + + rolling_cov_plot <- ggplot(pos_bias_df, aes(x = pos, y = rolling_mean_cov)) + + + # Add line for rolling mean coverage + geom_line(aes(color = "Coverage")) + + + # Add a horizontal line with a mapped linetype so it appears in the legend + geom_hline(aes(yintercept = ((21 * nchar(aa_seq) * targeted_counts_per_aa_variant)), + linetype = linetype_placeholder), + color = "black", linewidth = 0.3) + + + # Individual axis labels for this plot + xlab("Amino Acid Position") + + ylab("Coverage") + + + # Manually set color and fill labels + scale_color_manual(values = c("Coverage" = "black")) + # Color for line + + # Set the linetype with a label that uses the targeted threshold dynamically + scale_linetype_manual(values = c("Required coverage" = "dotted"), + labels = paste("Required coverage \\n for", as.character(targeted_counts_per_aa_variant), "counts per variant \\n if equally present")) + + + # Apply the saved theme and design elements at the end + plots_theme + + + # Set y-axis to log scale and apply comma formatting + scale_y_continuous(trans = 'log10', labels = scales::comma) + + + # Return the plot + ggsave(filename = output_file_path, plot = rolling_cov_plot, device = "pdf", width = 10, height = 6) +} + +##### +# run function +##### +position_biases( + prefiltered_gatk_path = "$variantCounts_filtered_by_library", + aa_seq_path = "$aa_seq", + window_size = $sliding_window_size, + output_file_path = "rolling_coverage.pdf", + targeted_counts_per_aa_variant = $aimed_cov +) + +##### +# create versions.yml +##### +r_version <- strsplit(version[['version.string']], ' ')[[1]][3] +dplyr_version <- as.character(packageVersion("dplyr")) +ggplot2_version <- as.character(packageVersion("ggplot2")) +scales_version <- as.character(packageVersion("scales")) +zoo_version <- as.character(packageVersion("zoo")) + +if (is.null(r_version)) r_version <- "unknown" +if (length(dplyr_version) == 0) dplyr_version <- "unknown" +if (length(ggplot2_version) == 0) ggplot2_version <- "unknown" +if (length(scales_version) == 0) scales_version <- "unknown" +if (length(zoo_version) == 0) zoo_version <- "unknown" + +f <- file("versions.yml", "w") +writeLines( + c( + '"${task.process}":', + paste(' r-base:', r_version), + paste(' r-dplyr:', dplyr_version), + paste(' r-ggplot2:', ggplot2_version), + paste(' r-scales:', scales_version), + paste(' r-zoo:', zoo_version) + ), + f +) +close(f) diff --git a/modules/local/visualization/logdiff/environment.yml b/modules/local/visualization/logdiff/environment.yml new file mode 100644 index 0000000..197283a --- /dev/null +++ b/modules/local/visualization/logdiff/environment.yml @@ -0,0 +1,15 @@ +channels: + - conda-forge + - bioconda +dependencies: + - bioconda::bioconductor-biostrings=2.78.0 + - conda-forge::r-base=4.5.1 + - conda-forge::r-biocmanager=1.30.27 + - conda-forge::r-dplyr=1.1.4 + - conda-forge::r-ggplot2=4.0.2 + - conda-forge::r-reshape2=1.4.4 + - conda-forge::r-scales=1.4.0 + - conda-forge::r-stringr=1.6.0 + - conda-forge::r-tidyr=1.3.1 + - conda-forge::r-tidyverse=2.0.0 + - conda-forge::r-zoo=1.8_15 diff --git a/modules/local/visualization/logdiff/main.nf b/modules/local/visualization/logdiff/main.nf new file mode 100644 index 0000000..3b733c5 --- /dev/null +++ b/modules/local/visualization/logdiff/main.nf @@ -0,0 +1,32 @@ +process VISUALIZATION_LOGDIFF { + tag "$meta.id" + label 'process_single' + + conda "${moduleDir}/environment.yml" + + container "${ workflow.containerEngine == 'singularity' + ? 'https://community-cr-prod.seqera.io/docker/registry/v2/blobs/sha256/73/73a72ec77725aeb67678a74228938fdd6827b669d01a8c96951b1a8ef96eeb0f/data' + : 'community.wave.seqera.io/library/bioconductor-biostrings_bioconductor-mutscan_r-base_r-biocmanager_pruned:c65036d76406f342' }" + + input: + tuple val(meta), path(library_completed_variantCounts) + + output: + tuple val(meta), path("logdiff_plot.pdf"), emit: logdiff_plot + tuple val(meta), path("logdiff_varying_bases.pdf"), emit: logdiff_varying_bases + path "versions.yml", emit: versions + + when: + task.ext.when == null || task.ext.when + + script: + template 'logdiff.R' + + stub: + """ + touch logdiff_plot.pdf + touch logdiff_varying_bases.pdf + echo "VISUALIZATION_COUNTS_HEATMAP:" > versions.yml + echo " stub-version: 0.0.0" >> versions.yml + """ +} diff --git a/modules/local/visualization/logdiff/meta.yml b/modules/local/visualization/logdiff/meta.yml new file mode 100644 index 0000000..dd4e343 --- /dev/null +++ b/modules/local/visualization/logdiff/meta.yml @@ -0,0 +1,54 @@ +name: "visualization_logdiff" +description: Generates diagnostic plots to visualize the logarithmic differences in variant counts, specifically focusing on the distribution of variants across different nucleotide compositions. +keywords: + - deep mutational scanning + - dms + - visualization + - diagnostics + - logdiff +tools: + - "mutscan": + description: "R package for analysis of deep mutational scanning data" + homepage: "https://bioconductor.org/packages/release/bioc/html/mutscan.html" + documentation: "https://bioconductor.org/packages/release/bioc/manuals/mutscan/man/mutscan.pdf" + tool_dev_url: "https://github.com/csoneson/mutscan" + doi: "10.1186/s12859-023-05187-y" + licence: ["GPL-3.0-or-later"] + +input: + - - meta: + type: map + description: | + Groovy Map containing sample information + e.g. `[ id:'test', single_end:false ]` + - library_completed_variantCounts: + type: file + description: CSV file containing processed variant counts, including zero-count entries from the library + pattern: "*.{csv}" + +output: + - logdiff_plot: + - meta: + type: map + description: Groovy Map containing sample information + - logdiff_plot.pdf: + type: file + description: PDF plot showing the overall logarithmic differences in variant counts + pattern: "logdiff_plot.pdf" + - logdiff_varying_bases: + - meta: + type: map + description: Groovy Map containing sample information + - logdiff_varying_bases.pdf: + type: file + description: PDF plot showing logarithmic differences categorized by varying nucleotide bases + pattern: "logdiff_varying_bases.pdf" + - versions: + - versions.yml: + type: file + description: File containing software versions + pattern: "versions.yml" + +authors: + - "@BenjaminWehnert1008" + - "@MaximilianStammnitz" diff --git a/modules/local/visualization/logdiff/templates/logdiff.R b/modules/local/visualization/logdiff/templates/logdiff.R new file mode 100644 index 0000000..752e574 --- /dev/null +++ b/modules/local/visualization/logdiff/templates/logdiff.R @@ -0,0 +1,115 @@ +#!/usr/bin/env Rscript + +# Maybe develop additional ideas to characterise variants below 10 percentile -> Do we find patterns that help the user to understand why certain variants are hard to count? + +# input: completed_prefiltered_gatk path, output folder path +# output: two logdiff-plots (counts_per_cov): 1st lineplot 2nd dotplot showing type of variant (varying bases - 1/2/3) + +library(dplyr) +library(ggplot2) +library(scales) + + logdiff_plot <- function(prefiltered_gatk_path) { + + # Load the data + prefiltered_gatk <- read.table(prefiltered_gatk_path, sep = ",", fill = NA, header = TRUE) + + # Sort by counts_per_cov while keeping corresponding varying_bases + sorted_counts <- prefiltered_gatk %>% + arrange(counts_per_cov) # Sort by counts_per_cov + + # Create a new column for the sorted index (1, 2, 3, ...) + sorted_counts\$ids <- 1:nrow(sorted_counts) + + # Calculate Q1 (10% quantile) and Q3 (90% quantile) + Q1 <- quantile(sorted_counts\$counts_per_cov, 0.10, na.rm = TRUE) + Q3 <- quantile(sorted_counts\$counts_per_cov, 0.90, na.rm = TRUE) + + # Calculate the LogDiff + LogDiff <- log10(Q3) - log10(Q1) + + # Create the first plot with a line + line_plot <- ggplot(sorted_counts, aes(x = ids, y = counts_per_cov)) + + geom_line(color = "black") + + + # Set axis labels + xlab("Variants") + + ylab("Counts per Coverage") + + + # Apply logarithmic scale to the y-axis + scale_y_continuous(trans = 'log10') + + + # Add horizontal dotted lines at Q1 and Q3 + geom_hline(yintercept = Q1, linetype = "dotted", color = "black") + + geom_hline(yintercept = Q3, linetype = "dotted", color = "black") + + + # Add the LogDiff value to the top left corner + annotate("text", x = 0, y = max(sorted_counts\$counts_per_cov), label = paste("LogDiff =", round(LogDiff, 2)), hjust = 0, vjust = 1, size = 5, color = "black") + + + # Apply the minimal theme + theme_minimal() + + # Save the line plot as a PDF + ggsave(filename = "logdiff_plot.pdf", plot = line_plot, device = "pdf", width = 10, height = 6) + + # Create the second plot with colored dots based on varying_bases + colored_plot <- ggplot(sorted_counts, aes(x = ids, y = counts_per_cov, color = as.factor(varying_bases))) + + + # Add horizontal dotted lines at Q1 and Q3 + geom_hline(yintercept = Q1, linetype = "dotted", color = "black") + + geom_hline(yintercept = Q3, linetype = "dotted", color = "black") + + + geom_point(size = 0.9) + # Use points instead of lines + + # Set axis labels + xlab("Variants") + + ylab("Counts per Coverage") + + + # Apply logarithmic scale to the y-axis + scale_y_continuous(trans = 'log10') + + + # Add the LogDiff value to the top left corner + annotate("text", x = 1, y = max(sorted_counts\$counts_per_cov), label = paste("LogDiff =", round(LogDiff, 2)), hjust = 0, vjust = 1, size = 5, color = "black") + + + # Add color scale for varying_bases + scale_color_manual(values = c("1" = "chocolate", "2" = "darkolivegreen3", "3" = "deepskyblue1"), name = "Varying \\n Bases") + + + # Apply the minimal theme + theme_minimal() + + # Save the colored plot as a PDF + ggsave(filename = "logdiff_varying_bases.pdf", plot = colored_plot, device = "pdf", width = 10, height = 6) + } + +##### +# run function +##### +logdiff_plot( + prefiltered_gatk_path = "$library_completed_variantCounts" +) + +#### +# create versions.yml +#### +r_version <- strsplit(version[['version.string']], ' ')[[1]][3] +dplyr_version <- as.character(packageVersion("dplyr")) +ggplot2_version <- as.character(packageVersion("ggplot2")) +scales_version <- as.character(packageVersion("scales")) + +if (is.null(r_version)) r_version <- "unknown" +if (length(dplyr_version) == 0) dplyr_version <- "unknown" +if (length(ggplot2_version) == 0) ggplot2_version <- "unknown" +if (length(scales_version) == 0) scales_version <- "unknown" + +f <- file("versions.yml", "w") +writeLines( + c( + '"${task.process}":', + paste(' r-base:', r_version), + paste(' r-dplyr:', dplyr_version), + paste(' r-ggplot2:', ggplot2_version), + paste(' r-scales:', scales_version) + ), + f +) +close(f) diff --git a/modules/local/visualization/seqdepth/environment.yml b/modules/local/visualization/seqdepth/environment.yml new file mode 100644 index 0000000..197283a --- /dev/null +++ b/modules/local/visualization/seqdepth/environment.yml @@ -0,0 +1,15 @@ +channels: + - conda-forge + - bioconda +dependencies: + - bioconda::bioconductor-biostrings=2.78.0 + - conda-forge::r-base=4.5.1 + - conda-forge::r-biocmanager=1.30.27 + - conda-forge::r-dplyr=1.1.4 + - conda-forge::r-ggplot2=4.0.2 + - conda-forge::r-reshape2=1.4.4 + - conda-forge::r-scales=1.4.0 + - conda-forge::r-stringr=1.6.0 + - conda-forge::r-tidyr=1.3.1 + - conda-forge::r-tidyverse=2.0.0 + - conda-forge::r-zoo=1.8_15 diff --git a/modules/local/visualization/seqdepth/main.nf b/modules/local/visualization/seqdepth/main.nf new file mode 100644 index 0000000..9d338c4 --- /dev/null +++ b/modules/local/visualization/seqdepth/main.nf @@ -0,0 +1,38 @@ +process VISUALIZATION_SEQDEPTH { + tag "$meta.id" + // Closed-form hypergeometric rarefaction - fast, so no longer a heavy process. + label 'process_single' + + conda "${moduleDir}/environment.yml" + + container "${ workflow.containerEngine == 'singularity' + ? 'https://community-cr-prod.seqera.io/docker/registry/v2/blobs/sha256/73/73a72ec77725aeb67678a74228938fdd6827b669d01a8c96951b1a8ef96eeb0f/data' + : 'community.wave.seqera.io/library/bioconductor-biostrings_bioconductor-mutscan_r-base_r-biocmanager_pruned:c65036d76406f342' }" + + input: + tuple val(meta), path(variantCounts_filtered_by_library) + path possible_mutations + val min_counts + + output: + tuple val(meta), path("SeqDepth.pdf"), emit: SeqDepth + tuple val(meta), path("seqdepth_curve.csv"), emit: curve + path "versions.yml", emit: versions + + when: + task.ext.when == null || task.ext.when + + script: + template 'SeqDepth_simulation.R' + + stub: + """ + touch SeqDepth.pdf + touch seqdepth_curve.csv + cat <<-END_VERSIONS > versions.yml + "${task.process}": + r-base: "0.0.0" + r-ggplot2: "0.0.0" + END_VERSIONS + """ +} diff --git a/modules/local/visualization/seqdepth/meta.yml b/modules/local/visualization/seqdepth/meta.yml new file mode 100644 index 0000000..e3a35c6 --- /dev/null +++ b/modules/local/visualization/seqdepth/meta.yml @@ -0,0 +1,55 @@ +name: "visualization_seqdepth" +description: Performs a sequencing depth simulation to assess library coverage and saturation, helping to determine if the sequencing effort was sufficient to capture all intended variants. +keywords: + - deep mutational scanning + - dms + - visualization + - sequencing depth + - saturation +tools: + - "mutscan": + description: "R package for analysis of deep mutational scanning data" + homepage: "https://bioconductor.org/packages/release/bioc/html/mutscan.html" + documentation: "https://bioconductor.org/packages/release/bioc/manuals/mutscan/man/mutscan.pdf" + tool_dev_url: "https://github.com/csoneson/mutscan" + doi: "10.1186/s12859-023-05187-y" + licence: ["GPL-3.0-or-later"] + +input: + - - meta: + type: map + description: | + Groovy Map containing sample information + e.g. `[ id:'test', single_end:false ]` + - variantCounts_filtered_by_library: + type: file + description: CSV file containing variant counts filtered against the target library + pattern: "*.{csv}" + - - possible_mutations: + type: file + description: CSV file defining all theoretically possible mutations for the library design + pattern: "*.{csv}" + - - min_counts: + type: integer + description: Minimum count threshold used in the simulation logic + +output: + - SeqDepth: + - meta: + type: map + description: | + Groovy Map containing sample information + e.g. `[ id:'test', single_end:false ]` + - SeqDepth.pdf: + type: file + description: PDF plot showing the simulation of variant detection as a function of sequencing depth + pattern: "SeqDepth.pdf" + - versions: + - versions.yml: + type: file + description: File containing software versions + pattern: "versions.yml" + +authors: + - "@BenjaminWehnert1008" + - "@MaximilianStammnitz" diff --git a/modules/local/visualization/seqdepth/templates/SeqDepth_simulation.R b/modules/local/visualization/seqdepth/templates/SeqDepth_simulation.R new file mode 100644 index 0000000..9a55b1b --- /dev/null +++ b/modules/local/visualization/seqdepth/templates/SeqDepth_simulation.R @@ -0,0 +1,106 @@ +#!/usr/bin/env Rscript + +# Sequencing-depth rarefaction: what fraction of the possible library variants stay detectable +# (>= `threshold` reads) as the sequencing depth is subsampled from full down to zero. +# +# This is a closed-form rarefaction, not a stochastic simulation. Subsampling reads without +# replacement makes each variant's surviving read count hypergeometric, so the expected number of +# variants still at or above the threshold at depth n is a sum of hypergeometric tail probabilities: +# +# E[detected | n reads] = sum_i P(Hypergeom(N, c_i, n) >= threshold) +# = sum_i phyper(threshold - 1, c_i, N - c_i, n, lower.tail = FALSE) +# +# where N is the total read count and c_i the reads on variant i. It is exact (no Monte-Carlo noise), +# needs no random sampling, and is orders of magnitude faster than draw-one-read-at-a-time loops - so +# the step count can be fine without the runtime blowing up. + +library(ggplot2) + +SeqDepth_simulation_plot <- function(prefiltered_gatk_path, possible_mutations_path, + output_file_path, curve_csv_path, + n_steps = 40, threshold = 3) { + + data <- read.csv(prefiltered_gatk_path) + counts <- as.numeric(data\$counts) + counts <- counts[!is.na(counts) & counts > 0] # only observed variants carry reads + possible_mutations <- read.csv(possible_mutations_path) + total_possible <- nrow(possible_mutations) # denominator: the whole designed library + + N <- sum(counts) + if (N <= 0 || total_possible <= 0) { + stop("SeqDepth: no counts or no possible mutations to rarefy") + } + + # Evenly spaced depths from 0 to the full library, as integer read totals. + depths <- unique(round(seq(0, N, length.out = n_steps + 1))) + + # Expected detected variants at each depth. phyper is vectorised over the per-variant counts, so each + # depth is one vectorised call; `lower.tail = FALSE` gives P(X > threshold-1) = P(X >= threshold). + expected_detected <- vapply(depths, function(n) { + if (n <= 0) return(0) + sum(phyper(threshold - 1, counts, N - counts, n, lower.tail = FALSE)) + }, numeric(1)) + + curve <- data.frame( + depth_fold = round(depths / N, 4), # x: fraction of full sequencing depth + variants_pct = round(expected_detected / total_possible * 100, 4) # y: % of the possible library + ) + write.csv(curve, curve_csv_path, row.names = FALSE) + + plot <- ggplot(curve, aes(x = depth_fold, y = variants_pct)) + + geom_line(color = "black", linewidth = 0.4) + + geom_hline(yintercept = 100, linetype = "dotted", color = "black") + + scale_y_continuous( + labels = scales::percent_format(scale = 1), + limits = c(0, 100), + breaks = seq(0, 100, by = 5) + ) + + scale_x_continuous( + limits = c(0, 1), + breaks = seq(0, 1, length.out = 20), + labels = scales::number_format(accuracy = 0.01) + ) + + labs(x = "Fraction of sequencing depth", y = "Variants detected (% of possible library)") + + theme_minimal() + + theme( + panel.border = element_rect(color = "black", fill = NA), + panel.grid.major = element_line(linewidth = 0.2, linetype = "solid", color = "grey80"), + panel.grid.minor = element_blank(), + axis.text.x = element_text(angle = 45, hjust = 1) + ) + + ggsave(output_file_path, plot = plot, device = "pdf", width = 8, height = 6) +} + + +##### +# run function +##### +SeqDepth_simulation_plot( + prefiltered_gatk_path = "$variantCounts_filtered_by_library", + possible_mutations_path = "$possible_mutations", + output_file_path = "SeqDepth.pdf", + curve_csv_path = "seqdepth_curve.csv", + n_steps = 40, + threshold = $min_counts + ) + +##### +# create versions.yml +##### +r_version <- strsplit(version[['version.string']], ' ')[[1]][3] +ggplot2_version <- as.character(packageVersion("ggplot2")) + +if (is.null(r_version)) r_version <- "unknown" +if (length(ggplot2_version) == 0) ggplot2_version <- "unknown" + +f <- file("versions.yml", "w") +writeLines( + c( + '"${task.process}":', + paste(' r-base:', r_version), + paste(' r-ggplot2:', ggplot2_version) + ), + f +) +close(f) diff --git a/modules/local/visualization/summary_report/environment.yml b/modules/local/visualization/summary_report/environment.yml new file mode 100644 index 0000000..b4d8e52 --- /dev/null +++ b/modules/local/visualization/summary_report/environment.yml @@ -0,0 +1,8 @@ +name: visualization_summary_report +channels: + - conda-forge + - bioconda +dependencies: + - python=3.12.3 + - pandas=2.2.1 + - numpy=1.26.4 diff --git a/modules/local/visualization/summary_report/main.nf b/modules/local/visualization/summary_report/main.nf new file mode 100644 index 0000000..25acef6 --- /dev/null +++ b/modules/local/visualization/summary_report/main.nf @@ -0,0 +1,43 @@ +// Build the single, self-contained all-in-one HTML report for a run. +// +// Every artefact the report shows is passed through one generic pair of inputs: a base64-encoded +// JSON manifest and the matching staged files, in the same order. That keeps the process signature +// stable no matter which optional parts of the pipeline ran - a section simply disappears when its +// entries are absent from the manifest, so no channel juggling is needed for --fitness/--pdb/EC. +process VISUALIZATION_SUMMARY_REPORT { + tag "${meta.sample ?: meta.id}" + label 'process_single' + + conda "${moduleDir}/environment.yml" + + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container + ? 'https://depot.galaxyproject.org/singularity/pandas:2.2.1' + : 'quay.io/biocontainers/pandas:2.2.1' }" + + input: + tuple val(meta), val(run_b64) // base64(JSON) run metadata (params, samples, versions) + val manifest_b64 // base64(JSON) [{kind,group,name}, ...] aligned with `assets` + path assets, stageAs: 'a*/*' // pdf/csv/tsv/html artefacts; numbered dirs avoid basename clashes + path template_html + path threedmol_js // 3Dmol.js library, inlined only when the run has a structure + + output: + tuple val(meta), path("deepmutscan_report.html"), emit: report + path "versions.yml" , emit: versions + + when: + task.ext.when == null || task.ext.when + + script: + template 'build_summary_report.py' + + stub: + """ + touch deepmutscan_report.html + cat <<-END_VERSIONS > versions.yml + "${task.process}": + python: "0.0.0" + pandas: "0.0.0" + END_VERSIONS + """ +} diff --git a/modules/local/visualization/summary_report/meta.yml b/modules/local/visualization/summary_report/meta.yml new file mode 100644 index 0000000..0b23fca --- /dev/null +++ b/modules/local/visualization/summary_report/meta.yml @@ -0,0 +1,60 @@ +name: "visualization_summary_report" +description: Build the single self-contained all-in-one HTML report for a run (QC to fitness) +keywords: + - report + - html + - quality control + - deep mutational scanning +tools: + - "pandas": + description: "Powerful data structures for data analysis, time series, and statistics" + homepage: "https://pandas.pydata.org/" + licence: ["BSD-3-Clause"] + +input: + - - meta: + type: map + description: | + Groovy Map containing sample information + e.g. `[ id:'test', sample:'ORF1' ]` + - run_b64: + type: string + description: Base64-encoded JSON with run-level metadata (parameters, samples, versions). + - - manifest_b64: + type: string + description: | + Base64-encoded JSON array describing every staged asset as `{kind, group, name}`, + in the same order as `assets`. Base64 keeps the JSON free of quoting problems when it is + interpolated into the template. + - - assets: + type: file + description: | + Every artefact embedded in the report (plot PDFs, count tables, the fitness table and the + detail HTML reports). Staged into numbered directories because basenames repeat across + samples. + - - template_html: + type: file + description: The report's HTML shell; data, plots and assets are injected into it. + pattern: "*.html" + +output: + - report: + - meta: + type: map + description: | + Groovy Map containing sample information + e.g. `[ id:'test', sample:'ORF1' ]` + - "deepmutscan_report.html": + type: file + description: Self-contained all-in-one report; no sidecar files are needed to view or share it. + pattern: "deepmutscan_report.html" + - versions: + - "versions.yml": + type: file + description: File containing software versions + pattern: "versions.yml" + +authors: + - "@BenjaminWehnert1008" +maintainers: + - "@BenjaminWehnert1008" diff --git a/modules/local/visualization/summary_report/templates/build_summary_report.py b/modules/local/visualization/summary_report/templates/build_summary_report.py new file mode 100644 index 0000000..ea17fc5 --- /dev/null +++ b/modules/local/visualization/summary_report/templates/build_summary_report.py @@ -0,0 +1,766 @@ +#!/usr/bin/env python3 +"""Build the self-contained all-in-one HTML report for nf-core/deepmutscan. + +Nextflow ``template`` script for the VISUALIZATION_SUMMARY_REPORT module. Everything the report +shows arrives through one generic mechanism: a base64 JSON manifest describing each staged asset +(`kind|group|name`) plus the assets themselves, in the same order. Sections that have no entries +are simply omitted, so the same code serves runs with or without fitness / error correction / a PDB. + +Two kinds of payload end up in the HTML: + * data - count tables and the fitness table, aggregated down to what the plots actually need, + then rendered natively as interactive SVG in the browser. + * blobs - the original R-generated plot PDFs and the standalone detail reports, embedded as + base64 data URIs so the single file needs no sidecars to view, download or share. + +Note: this is a Nextflow template, so every backslash meant for Python is doubled and literal +dollar signs are escaped. +""" +import base64 +import json +import platform +import re +from pathlib import Path + +import numpy as np +import pandas as pd + +MUT = re.compile(r"^([A-Za-z*]+)(\\d+)([A-Za-z*]+)") +REP_RE = re.compile(r"^rescaled_fitness_rep(\\d+)\$", re.I) +IN_RE = re.compile(r"^input(\\d+)\$", re.I) +OUT_RE = re.compile(r"^output(\\d+)\$", re.I) + +# Reference data for the citation page. Every entry is transcribed from CITATIONS.md, which is the +# pipeline's own maintained list - the point is that the report and the repo can never drift apart, +# and that nothing here is invented. `pkgs` are the names these tools use in the run's versions.yml, +# which is what decides whether a tool actually ran and therefore appears. +CITATIONS = [ + {"id": "deepmutscan", "always": True, "pkgs": [], + "authors": "Wehnert B, et al.", "title": "nf-core/deepmutscan", + "journal": "", "year": "", "key": "deepmutscan", + "note": "bioRxiv preprint, coming soon."}, + {"id": "nfcore", "always": True, "pkgs": [], + "authors": "Ewels PA, Peltzer A, Fillinger S, Patel H, Alneberg J, Wilm A, Garcia MU, Di Tommaso P, Nahnsen S", + "title": "The nf-core framework for community-curated bioinformatics pipelines", + "journal": "Nature Biotechnology", "year": "2020", "volume": "38", "number": "3", "pages": "276--278", + "doi": "10.1038/s41587-020-0439-x", "key": "Ewels2020"}, + {"id": "nextflow", "always": True, "pkgs": ["nextflow"], + "authors": "Di Tommaso P, Chatzou M, Floden EW, Barja PP, Palumbo E, Notredame C", + "title": "Nextflow enables reproducible computational workflows", + "journal": "Nature Biotechnology", "year": "2017", "volume": "35", "number": "4", "pages": "316--319", + "doi": "10.1038/nbt.3820", "key": "DiTommaso2017"}, + {"id": "fastqc", "pkgs": ["fastqc"], + "authors": "Andrews S", "title": "FastQC: A Quality Control Tool for High Throughput Sequence Data", + "year": "2010", "url": "https://www.bioinformatics.babraham.ac.uk/projects/fastqc/", "key": "Andrews2010"}, + {"id": "multiqc", "pkgs": ["multiqc"], + "authors": "Ewels P, Magnusson M, Lundin S, K\\u00e4ller M", + "title": "MultiQC: summarize analysis results for multiple tools and samples in a single report", + "journal": "Bioinformatics", "year": "2016", "volume": "32", "number": "19", "pages": "3047--3048", + "doi": "10.1093/bioinformatics/btw354", "key": "Ewels2016"}, + {"id": "samtools", "pkgs": ["samtools"], + "authors": "Danecek P, Bonfield JK, Liddle J, Marshall J, Ohan V, Pollard MO, Whitwham A, Keane T, McCarthy SA, Davies RM, Li H", + "title": "Twelve years of SAMtools and BCFtools", + "journal": "GigaScience", "year": "2021", "volume": "10", "number": "2", "pages": "giab008", + "doi": "10.1093/gigascience/giab008", "key": "Danecek2021"}, + # BWA-MEM, not BWA-backtrack: the pipeline runs `bwa mem`, whose citation is the 2013 arXiv + # preprint - the same one nf-core's own bwa/mem module points at. Li & Durbin 2009 describes + # `bwa aln`, a different algorithm this pipeline never calls. + {"id": "bwa", "pkgs": ["bwa"], + "authors": "Li H", "title": "Aligning sequence reads, clone sequences and assembly contigs with BWA-MEM", + "journal": "arXiv", "year": "2013", "eprint": "1303.3997", "primaryclass": "q-bio.GN", + "url": "https://arxiv.org/abs/1303.3997", "key": "Li2013"}, + {"id": "pysam", "pkgs": ["pysam"], + "authors": "Heger A, et al.", "title": "pysam: a Python module for reading, manipulating and writing genomic data sets", + "year": "", "url": "https://github.com/pysam-developers/pysam", "key": "pysam"}, + {"id": "polars", "pkgs": ["polars"], + "authors": "Vink R, et al.", "title": "Polars: Lightning-fast DataFrame library for Rust and Python", + "year": "", "url": "https://github.com/pola-rs/polars", "key": "polars"}, + {"id": "biopython", "pkgs": ["biopython"], + "authors": "Cock PJA, Antao T, Chang JT, Chapman BA, Cox CJ, Dalke A, Friedberg I, Hamelryck T, Kauff F, Wilczynski B, de Hoon MJL", + "title": "Biopython: freely available Python tools for computational molecular biology and bioinformatics", + "journal": "Bioinformatics", "year": "2009", "volume": "25", "number": "11", "pages": "1422--1423", + "doi": "10.1093/bioinformatics/btp163", "key": "Cock2009"}, + {"id": "threedmol", "pkgs": ["3dmol"], + "authors": "Rego N, Koes D", "title": "3Dmol.js: molecular visualization with WebGL", + "journal": "Bioinformatics", "year": "2015", "volume": "31", "number": "8", "pages": "1322--1324", + "doi": "10.1093/bioinformatics/btu829", "key": "Rego2015", + "note": "Bundled in the interactive variant effect inspection tool (BSD-3-Clause)."}, + {"id": "dimsum", "pkgs": ["r-dimsum", "dimsum"], + "authors": "Faure AJ, Schmiedel JM, Baeza-Centurion P, Lehner B", + "title": "DiMSum: an error model and pipeline for analyzing deep mutational scanning data and diagnosing common experimental pathologies", + "journal": "Genome Biology", "year": "2020", "volume": "21", "number": "1", "pages": "207", + "doi": "10.1186/s13059-020-02091-3", "key": "Faure2020"}, + {"id": "mutscan", "pkgs": ["r-mutscan", "mutscan"], + "authors": "Soneson C, Bendel AM, Diss G, Stadler MB", + "title": "mutscan - a flexible R package for efficient end-to-end analysis of multiplexed assays of variant effect data", + "journal": "Genome Biology", "year": "2023", "volume": "24", "number": "1", "pages": "132", + "doi": "10.1186/s13059-023-02967-0", "key": "Soneson2023"}, +] + + +class args: + run = "$run_b64" + manifest = "$manifest_b64" + assets = "$assets".split() + template = "$template_html" + threedmol = "$threedmol_js" # 3Dmol.js, inlined only when a structure is shown + out_html = "deepmutscan_report.html" + + +def b64json(s): + return json.loads(base64.b64decode(s).decode("utf-8")) + + +def data_uri(path, mime): + return "data:" + mime + ";base64," + base64.b64encode(Path(path).read_bytes()).decode("ascii") + + +# Embedded reports run in a frame whose origin is opaque, and in an opaque origin `localStorage` and +# `document.cookie` do not return empty - they *throw* SecurityError. MultiQC v1.35 reads both while +# bootstrapping without a try/catch, so the exception unwinds its ready handler and the report is left +# frozen on its "Loading report.." placeholder with no plots. Handing it a memory-backed store that +# never throws is enough; the only thing lost is persistence of the reader's MultiQC preferences. +# This prepends to the file we embed and does not touch the published multiqc_report.html. +STORAGE_SHIM = """""" + +HEAD_RE = re.compile(r"]*>", re.I) + + +def html_for_frame(path): + """Read an embedded report and make it survive an opaque origin.""" + text = Path(path).read_text(encoding="utf-8", errors="surrogateescape") + m = HEAD_RE.search(text) + if m: + text = text[: m.end()] + STORAGE_SHIM + text[m.end():] + return text + + +def r6(x): + return round(float(x), 6) if x is not None and x == x else None + + +def to_float(x): + try: + v = float(x) + return round(v, 6) if v == v else None + except (TypeError, ValueError): + return None + + +def wt_orf(fasta_path, reading_frame): + """Wildtype ORF nucleotides plus the reference offset they start at. + + --reading_frame is the 1-based inclusive range the pipeline uses everywhere else (e.g. + "352-1383"), converted to a 0-based half-open slice here. `start` is kept so nucleotide positions + can be reported in reference coordinates, which is what base_mut uses. + + Returns None rather than guessing whenever anything fails to line up: a wrong offset would + mislabel every nucleotide change while still looking entirely plausible. + """ + if not fasta_path or not reading_frame: + return None + try: + lo_s, hi_s = str(reading_frame).split("-") + lo, hi = int(lo_s), int(hi_s) + except (TypeError, ValueError): + return None + seq = "".join(l.strip() for l in Path(fasta_path).read_text().splitlines() + if l.strip() and not l.startswith(">")).upper() + if lo < 1 or hi > len(seq) or hi < lo: + return None + orf = seq[lo - 1:hi] + if len(orf) % 3: + return None + return {"seq": orf, "start": lo} + + +def parse_pos(s): + m = MUT.match(str(s)) + return (m.group(1), int(m.group(2)), m.group(3)) if m else (None, None, None) + + +def num(df, name): + return (pd.to_numeric(df[name], errors="coerce") if name in df.columns + else pd.Series([np.nan] * len(df))) + + +def per_position(path, orf_len): + """Aggregate a filtered-by-library table to per-ORF-position series. + + Deliberately mirrors `global_position_biases_{counts,cov}.R` so the interactive curves and the + pipeline's published PDFs describe the same quantity: + + * coverage - a property of the position, so it is averaged over the substitutions measured + there (the R comment claims it is constant; on real data it varies by ~0.25% + because reads must span every varying base plus --min_flank, which is small + enough that the mean is the right summary). + * counts / counts_per_cov + - averaged *within* a varying-bases class and never pooled across classes. The + classes differ several-fold in both counts and frequency, and their mix changes + along the ORF, so a pooled curve would track composition rather than the library. + * R uses na.rm=FALSE for these per-position means, hence skipna=False here. + + Series run over every ORF position 1..orf_len, with None where a class has no variants, so that + the rolling mean in the browser sees the same gaps rollapply() does. + + `counts_raw` is only present once error correction has run, and drives the per-sample summary. + """ + df = pd.read_csv(path) + pos = df["pos_mut"].astype(str).map(lambda s: parse_pos(s)[1]) + cov, cnt = num(df, "cov"), num(df, "counts") + cpc, raw = num(df, "counts_per_cov"), num(df, "counts_raw") + vb = num(df, "varying_bases") + d = pd.DataFrame({"pos": pos, "cov": cov, "counts": cnt, "cpc": cpc, "raw": raw, "vb": vb}) + d = d.dropna(subset=["pos"]) + d["pos"] = d["pos"].astype(int) + + n = int(orf_len) if orf_len else (int(d["pos"].max()) if len(d) else 0) + axis = list(range(1, n + 1)) + + cov_by = d.groupby("pos")["cov"].mean(numeric_only=False) + positions = [{"pos": p, "cov": r6(cov_by.get(p))} for p in axis] + + classes = {} + for k in (1, 2, 3): + sub = d[d["vb"] == k] + cm = sub.groupby("pos")["counts"].apply(lambda s: s.mean(skipna=False)) + fm = sub.groupby("pos")["cpc"].apply(lambda s: s.mean(skipna=False)) + classes[str(k)] = [{"pos": p, "counts": r6(cm.get(p)), "cpc": r6(fm.get(p))} for p in axis] + + ec = None + if raw.notna().any(): + tr, tc = float(np.nansum(raw.values)), float(np.nansum(cnt.values)) + ec = { + "n_variants": int(len(d)), + "n_corrected": int((raw.fillna(0) != cnt.fillna(0)).sum()), + "total_raw": round(tr, 1), + "total_corrected": round(tc, 1), + "pct_removed": round((tr - tc) / tr * 100.0, 2) if tr > 0 else 0.0, + } + return positions, classes, ec + + +def heatmap(path): + """Counts-per-coverage grid (position x substituted amino acid) for one sample.""" + df = pd.read_csv(path) + if "position" not in df.columns or "mut_aa" not in df.columns: + return None + pos = pd.to_numeric(df["position"], errors="coerce") + val = num(df, "total_counts_per_cov") + cells = [{"p": int(p), "a": str(a), "v": r6(v)} + for p, a, v in zip(pos, df["mut_aa"].astype(str), val) + if p == p and v == v] + wt = {} + if "wt_aa" in df.columns: + for p, w in zip(pos, df["wt_aa"].astype(str)): + if p == p and w and w != "nan": + wt[int(p)] = w + return {"cells": cells, "wt": wt} + + +def codon_changes(nt_seq_field, pos, wt_nt): + """Nucleotide changes behind one amino-acid substitution. + + `nt_seq` holds the full ORF for every distinct coding variant that produces this amino-acid + change, comma-separated - an NNK library typically reaches one residue through several codons. + Each is diffed against the wildtype ORF over this residue's codon only: changes elsewhere belong + to other residues and would be double counted. Returns one entry per distinct codon. + + Reported positions are in REFERENCE coordinates, not ORF-relative ones, because that is the + convention `base_mut` uses everywhere else in the pipeline - checked against variantCounts, where + 4000/4000 variants agree with the reference convention and 0/4000 with the ORF-relative one. + Emitting ORF-relative numbers here would be off by the reading frame's start and would silently + disagree with the error-correction report. + """ + if not wt_nt or not wt_nt.get("seq") or not nt_seq_field: + return [] + seq_wt, rf_lo = wt_nt["seq"], wt_nt["start"] + lo = (int(pos) - 1) * 3 + if lo + 3 > len(seq_wt): + return [] + wt_codon = seq_wt[lo:lo + 3] + out, seen = [], set() + for seq in (s.strip() for s in str(nt_seq_field).split(",")): + if len(seq) < lo + 3: + continue + mut_codon = seq[lo:lo + 3] + if mut_codon == wt_codon or mut_codon in seen: + continue + seen.add(mut_codon) + changes, positions = [], [] + for k in range(3): + if wt_codon[k] != mut_codon[k]: + changes.append(f"{wt_codon[k]}>{mut_codon[k]}") + positions.append(rf_lo + lo + k) # reference coords, as base_mut reports them + out.append({"wt_codon": wt_codon, "mut_codon": mut_codon, + "changes": changes, "positions": positions}) + return out + + +def load_fitness(path, wt_nt=None): + """Per-substitution fitness plus its per-position mean, from the default fitness table.""" + df = pd.read_csv(path, sep="\\t", dtype=str, keep_default_na=False) + + def col(n): + return next((c for c in df.columns if c.strip().lower() == n), None) + + fc, sc = col("mean fitness"), col("fitness sd") + if not fc: + return None + # Replicate count is a per-run property, so discover the columns instead of assuming rep1/rep2. + rep_cols = sorted((c for c in df.columns if REP_RE.match(c.strip())), + key=lambda c: int(REP_RE.match(c.strip()).group(1))) + in_cols = [c for c in df.columns if IN_RE.match(c.strip())] + out_cols = [c for c in df.columns if OUT_RE.match(c.strip())] + ntc = col("nt_seq") + rows, per_pos = [], {} + for _, r in df.iterrows(): + try: + p = int(float(r.get("pos", "") or "nan")) + except (TypeError, ValueError): + continue + wt, mut = (r.get("wt_aa", "") or "").strip(), (r.get("mut_aa", "") or "").strip() + if not wt or not mut: + continue + try: + f = float(r.get(fc, "")) + except (TypeError, ValueError): + continue + if f != f: + continue + sd = None + try: + sd = float(r.get(sc, "")) if sc else None + if sd != sd: + sd = None + except (TypeError, ValueError): + sd = None + stop = mut == "*" or (r.get("stop", "").strip() not in ("", "0")) + + def tot(cols): + s = 0.0 + got = False + for c in cols: + try: + s += float(r.get(c, "") or 0) + got = True + except (TypeError, ValueError): + pass + return s if got else None + + cell = {"p": p, "wt": wt, "a": mut, "f": r6(f), "sd": r6(sd), + "syn": mut == wt, "stop": bool(stop)} + if rep_cols: + cell["reps"] = [to_float(r.get(c, "")) for c in rep_cols] + cell["n_in"], cell["n_out"] = tot(in_cols), tot(out_cols) + if ntc and wt_nt: + nt = codon_changes(r.get(ntc, ""), p, wt_nt) + if nt: + cell["nt"] = nt + rows.append(cell) + if mut != wt and not stop: + per_pos.setdefault(p, []).append(f) + return {"cells": rows} + + +THREE_TO_ONE = { + "ALA": "A", "ARG": "R", "ASN": "N", "ASP": "D", "CYS": "C", "GLN": "Q", "GLU": "E", + "GLY": "G", "HIS": "H", "ILE": "I", "LEU": "L", "LYS": "K", "MET": "M", "PHE": "F", + "PRO": "P", "SER": "S", "THR": "T", "TRP": "W", "TYR": "Y", "VAL": "V", +} + + +def parse_pdb(pdb_path): + """(pdb_text, residues) with residues = [{resnum, aa}] from the CA atoms, ordered by residue number.""" + text = Path(pdb_path).read_text() + residues, seen = [], set() + for line in text.splitlines(): + if not line.startswith("ATOM") or line[12:16].strip() != "CA": + continue + try: + resnum = int(line[22:26]) + except ValueError: + continue + if resnum in seen: + continue + seen.add(resnum) + residues.append({"resnum": resnum, "aa": THREE_TO_ONE.get(line[17:20].strip().upper(), "X")}) + residues.sort(key=lambda r: r["resnum"]) + return text, residues + + +def best_alignment(wt_seq, struct_residues): + """Offset mapping wt position j (0-based) to struct_residues[off0 + j], maximising identity. + + The PDB's residue numbering need not start at the ORF's first residue (AFDB models often carry a + signal peptide or a different frame), so the mapping is found by sliding the sequences, exactly as + the inspection tool does - the two must agree or the structure would be coloured off by a frame. + """ + struct_seq = "".join(r["aa"] for r in struct_residues) + n, m = len(wt_seq), len(struct_seq) + best_off, best_matches = 0, -1 + for off in range(-n + 1, m): + matches = sum(1 for j in range(n) if 0 <= off + j < m and struct_seq[off + j] == wt_seq[j]) + if matches > best_matches: + best_matches, best_off = matches, off + return best_off, best_matches / max(1, n) + + +def structure_data(pdb_path, wt_seq, fitness): + """PDB text plus per-residue mean fitness, keyed by PDB residue number, for the spinning viewer. + + The colour at a residue is the mean fitness over the missense substitutions measured there - the + same position summary the fitness heatmap collapses to - so the structure and the heatmap tell one + story. Returns None (viewer simply hidden) whenever a structure cannot be trusted: no PDB, no + fitness, or an alignment that never lines up. + """ + if not pdb_path or not wt_seq or not fitness or not fitness.get("cells"): + return None + pdb_text, residues = parse_pdb(pdb_path) + if not residues: + return None + off0, identity = best_alignment(wt_seq, residues) + by_pos = {} + for c in fitness["cells"]: + if c.get("syn") or c.get("stop") or c.get("f") is None: + continue + by_pos.setdefault(c["p"], []).append(c["f"]) + pos_mean = {p: sum(v) / len(v) for p, v in by_pos.items() if v} + resi_fit = {} + for j in range(len(wt_seq)): + k = off0 + j + if 0 <= k < len(residues) and (j + 1) in pos_mean: + resi_fit[residues[k]["resnum"]] = round(pos_mean[j + 1], 4) + if not resi_fit: + return None + fmax = max(abs(v) for v in resi_fit.values()) or 1.0 + return {"resi_fit": resi_fit, "fmax": round(fmax, 4), + "identity": round(identity, 4), "n_residues": len(residues), "pdb": pdb_text} + + +def _dur_s(s): + """Nextflow-formatted duration ('7m 37s', '746ms', '1h 2m') to seconds.""" + s = str(s).strip() + if not s or s == "-": + return 0.0 + tot = 0.0 + for num, unit in re.findall(r"([\\d.]+)\\s*(ms|s|m|h|d)", s): + tot += float(num) * {"ms": 0.001, "s": 1, "m": 60, "h": 3600, "d": 86400}[unit] + return tot + + +def _size_b(s): + """Nextflow-formatted size ('3 GB', '4.9 MB') to bytes.""" + m = re.match(r"([\\d.]+)\\s*(B|KB|MB|GB|TB)", str(s).strip(), re.I) + if not m: + return 0.0 + return float(m.group(1)) * {"B": 1, "KB": 1e3, "MB": 1e6, "GB": 1e9, "TB": 1e12}[m.group(2).upper()] + + +def run_stats(path): + """Per-process compute stats from Nextflow's execution trace. + + The trace is written continuously, so at report time it holds every task finished so far - i.e. + everything but the report itself and the version collation that follows it. That is the whole + compute-heavy body of the run, which is what the page is about; it is labelled 'as of report + generation' rather than pretended to be the final total. CACHED tasks are counted separately + because they consumed no compute this run. + """ + df = pd.read_csv(path, sep="\\t", dtype=str, keep_default_na=False) + if "name" not in df.columns: + return None + + def proc(n): + return re.sub(r"\\s*\\(.*\\)\\s*\$", "", n).split(":")[-1] # drop "(sample)" and the module path + + rows = {} + for _, r in df.iterrows(): + p = proc(r.get("name", "")) + d = rows.setdefault(p, {"n": 0, "cached": 0, "cpu_time": 0.0, "rss": 0.0, "cpu": [], "submit": ""}) + d["n"] += 1 + if r.get("status", "") == "CACHED": + d["cached"] += 1 + d["cpu_time"] += _dur_s(r.get("realtime", "")) + d["rss"] = max(d["rss"], _size_b(r.get("peak_rss", ""))) + # earliest submission time, so the table can read top-to-bottom in run order. The trace timestamp + # is "YYYY-MM-DD HH:MM:SS.mmm", which sorts lexically = chronologically, so no parsing is needed. + sub = r.get("submit", "") + if sub and (not d["submit"] or sub < d["submit"]): + d["submit"] = sub + c = r.get("%cpu", "").replace("%", "").strip() + try: + d["cpu"].append(float(c)) + except ValueError: + pass + procs = [{ + "process": p, "tasks": d["n"], "cached": d["cached"], + "cpu_time": round(d["cpu_time"], 1), "peak_rss": round(d["rss"]), + "cpu_pct": round(sum(d["cpu"]) / len(d["cpu"])) if d["cpu"] else None, + "submit": d["submit"], + } for p, d in rows.items()] + # chronological: first-submitted process first (falls back to name for any row with no timestamp) + procs.sort(key=lambda x: (x["submit"] or "~", x["process"])) + status = df["status"] if "status" in df.columns else pd.Series([], dtype=str) + total = { + "tasks": int(df.shape[0]), + "cached": int((status == "CACHED").sum()), + "cpu_time": round(sum(p["cpu_time"] for p in procs), 1), + "peak_rss": round(max((p["peak_rss"] for p in procs), default=0)), + } + return {"processes": procs, "total": total} + + +def software_versions(path): + """Parse pipeline_info's software versions YAML into {process: {package: version}}. + + Hand-parsed rather than via a YAML library: the file is two levels of trivial `key: value` and + pandas' container carries no yaml. Duplicate process keys are merged rather than overwritten - + they occur in practice when a module fails to interpolate its own name. + """ + out = {} + cur = None + for line in Path(path).read_text().splitlines(): + if not line.strip() or line.lstrip().startswith("#"): + continue + if not line.startswith((" ", "\\t")): + cur = line.strip().rstrip(":").strip('"') + out.setdefault(cur, {}) + elif cur: + k, _, v = line.strip().partition(":") + if k: + out[cur][k.strip()] = v.strip() + return out + + +def citations(versions, extra=()): + """The subset of CITATIONS whose tools actually ran, plus the pipeline and frameworks always. + + Package names in versions.yml are the primary signal, but they are not always sufficient: DiMSum, + for instance, reports only `r-base`, so it is invisible at the package level. `extra` carries the + tokens that a run's own flags settle unambiguously (--dimsum, --mutscan, a --pdb structure). A tool + outside both signals is never cited - the page is about this run, not the pipeline's full + repertoire. + """ + seen = {p.strip().lower() for pkgs in versions.values() for p in pkgs} + seen |= {e.lower() for e in extra} + out = [] + for c in CITATIONS: + if c.get("always") or any(p.lower() in seen for p in c["pkgs"]): + out.append({k: v for k, v in c.items() if k != "pkgs"}) + return out + + +def biblatex(entries): + """A biblatex source block for the given entries.""" + # The braces are assembled from these two names instead of being written literally: nf-core's + # `template_strings` lint reads any doubled-brace pair in the source as a leftover Jinja + # placeholder, and biblatex needs exactly such a pair around the title to protect its + # capitalisation. + ob, cb = "{", "}" + lines = [] + for c in entries: + kind = "@article" + if c.get("eprint"): + kind = "@misc" + elif not c.get("journal"): + kind = "@misc" + lines.append(kind + ob + c["key"] + ",") + lines.append(" author = " + ob + c["authors"].replace(", ", " and ") + cb + ",") + lines.append(" title = " + ob + ob + c["title"] + cb + cb + ",") + for fld, key in (("journaltitle", "journal"), ("year", "year"), ("volume", "volume"), + ("number", "number"), ("pages", "pages"), ("doi", "doi"), + ("eprint", "eprint"), ("eprintclass", "primaryclass"), ("url", "url")): + if c.get(key): + lines.append(f" {fld:<12} = " + ob + str(c[key]) + cb + ",") + if c.get("note"): + lines.append(" note = " + ob + c["note"] + cb + ",") + lines.append("}") + lines.append("") + return "\\n".join(lines) + + +def main(): + run = b64json(args.run) + manifest = b64json(args.manifest) + if len(manifest) != len(args.assets): + raise SystemExit(f"manifest has {len(manifest)} entries but {len(args.assets)} files were staged") + + samples, plots, reports, fitness, dl, versions = {}, {}, [], None, [], {} + + def sample(sid): + # type/replicate drive the input-vs-output colour palette and the seqdepth default selection. + # Pipeline ids are "___", so the type is a delimited token - matching it + # that way is robust for any protein name (which is a separate, earlier segment). + m = re.search(r"_(input|output|wildtype|quality)_(\\d+)", sid) + stype = m.group(1) if m else None + srep = int(m.group(2)) if m else None + return samples.setdefault(sid, {"id": sid, "type": stype, "replicate": srep, + "positions": [], "classes": {}, "heatmap": None, "ec": None, "seqdepth": None}) + + # The ORF length has to come from the wildtype protein sequence, not from the highest position that + # happens to carry a variant, or trailing residues with no library coverage would silently shorten + # the axis and shift every rolling window. + orf_len, wt_nt, wt_aa_seq, pdb_path = 0, None, None, None + for entry, path in zip(manifest, args.assets): + if entry["kind"] == "aa_seq": + wt_aa_seq = Path(path).read_text().strip().split("\\n")[0].strip() + orf_len = len(wt_aa_seq) + elif entry["kind"] == "fasta": + wt_nt = wt_orf(path, run.get("reading_frame")) + elif entry["kind"] == "pdb": + pdb_path = path + # A frame that disagrees with the protein length means the codon diffs would be off by a whole + # residue, so drop the nucleotide detail rather than print something plausible and wrong. + if wt_nt and orf_len and len(wt_nt["seq"]) // 3 not in (orf_len, orf_len + 1): + print(f"WARNING: ORF is {len(wt_nt['seq'])} nt ({len(wt_nt['seq'])//3} codons) but the protein is " + f"{orf_len} aa; skipping nucleotide-level detail") + wt_nt = None + + for entry, path in zip(manifest, args.assets): + kind, group, name = entry["kind"], entry["group"], entry["name"] + + if kind == "qc": # per-sample library-QC plot (original R PDF) + # Deliberately NOT embedded. The report re-plots these natively and offers the live chart + # as SVG/PNG, so carrying ~25 MB of base64 PDF bought nothing but weight - and the weight + # is not free: it is what stops the embedded MultiQC report from finishing its render. + # Only the location is kept, for the "open the results folder" links. + plots.setdefault(group, {})[Path(name).stem] = None + + elif kind == "filtered": # per-sample counts -> per-position curves + EC summary + positions, classes, ec = per_position(path, orf_len) + s = sample(group) + s["positions"], s["classes"], s["ec"] = positions, classes, ec + + elif kind == "heatmap": # per-sample counts-per-cov grid + sample(group)["heatmap"] = heatmap(path) + + elif kind == "seqdepth": # per-sample rarefaction curve (depth fraction -> % library) + sd = pd.read_csv(path) + sample(group)["seqdepth"] = [ + {"x": r6(x), "y": r6(y)} + for x, y in zip(num(sd, "depth_fold"), num(sd, "variants_pct")) + if x == x and y == y + ] + + elif kind == "fitness_table": + fitness = load_fitness(path, wt_nt) + + elif kind in ("fitness_pdf", "mutscan_pdf"): + # Same reasoning as the QC PDFs: recorded by name so the page can link to them in the + # results folder, not inlined. + dl.append({"section": "fitness", "group": group, "name": name}) + + elif kind == "report": # standalone detail report, embedded for a lazy iframe + # Carried as srcdoc text rather than a data: URI. srcdoc inherits this document's origin + # instead of minting an opaque one, and it is ~25% smaller than the base64 equivalent. + reports.append({"group": group, "name": name, "html": html_for_frame(path)}) + + elif kind == "versions": # pipeline_info software-versions YAML + versions = software_versions(path) + + elif kind == "trace": # Nextflow execution trace -> per-process compute stats + try: + run["stats"] = run_stats(path) + except Exception as exc: # a malformed/partial trace must never sink the whole report + print(f"WARNING: could not parse execution trace: {exc}") + + # Coverage needed for every residue outcome to reach --aimed_cov counts, assuming all 21 outcomes + # are equally represented. Same expression as the reference line in global_position_biases_cov.R; + # it is a per-position number because each read spans the whole ORF in this assay. + try: + run["required_cov"] = 21 * orf_len * float(run.get("aimed_cov") or 0) or None + except (TypeError, ValueError): + run["required_cov"] = None + run["orf_length"] = orf_len + + # Flags settle the tools that the version strings alone cannot: DiMSum reports only r-base, and + # 3Dmol.js has no package entry at all - the structure viewer's presence is the tell. + def truthy(x): + return str(x).strip().lower() in ("true", "1", "yes") + + extra = set() + if truthy(run.get("dimsum")): + extra.add("dimsum") + if truthy(run.get("mutscan")): + extra.add("mutscan") + if any(r["group"] == "viewer" for r in reports): + extra.add("3dmol") + if pdb_path: + extra.add("3dmol") + cites = citations(versions, extra) + # The spinning structure needs a PDB, fitness, and the wildtype protein sequence to align them; any + # missing and the viewer is simply left out (structure = None) so the page still renders. + structure = structure_data(pdb_path, wt_aa_seq, fitness) + data = { + "run": run, + "samples": [samples[k] for k in sorted(samples)], + "plots": plots, + "reports": reports, + "fitness": fitness, + "structure": structure, + "downloads": dl, + # Two citation groups for the page: the pipeline itself (always) and the tools that actually + # ran. Each carries its own biblatex block so the "get biblatex" button has nothing to compute. + # Section 1 is the pipeline and the frameworks it is built on; section 2 is the analysis tools + # this particular run actually invoked. + "citations": (lambda pipe: { + "pipeline": [c for c in cites if c["id"] in pipe], + "tools": [c for c in cites if c["id"] not in pipe], + "biblatex_pipeline": biblatex([c for c in cites if c["id"] in pipe]), + "biblatex_tools": biblatex([c for c in cites if c["id"] not in pipe]), + })({"deepmutscan", "nfcore", "nextflow"}), + "software": versions, + } + + # 3Dmol.js is ~0.5 MB, so inline it only when a structure will actually be shown; otherwise the + # placeholder collapses to nothing and a no-PDB run carries no dead weight. + threedmol_js = "" + if structure and args.threedmol: + try: + threedmol_js = Path(args.threedmol).read_text(encoding="utf-8", errors="surrogateescape") + except OSError: + threedmol_js = "" + + html = (Path(args.template).read_text(encoding="utf-8") + .replace("__REPORT_DATA__", json.dumps(data, separators=(",", ":")).replace("&1 | sed -n "s/^Version: //p"'), topic: versions, emit: versions_bwa + + when: + task.ext.when == null || task.ext.when + + script: + def prefix = task.ext.prefix ?: "${fasta.baseName}" + def args = task.ext.args ?: '' + """ + mkdir bwa + bwa \\ + index \\ + $args \\ + -p bwa/${prefix} \\ + $fasta + """ + + stub: + def prefix = task.ext.prefix ?: "${fasta.baseName}" + """ + mkdir bwa + touch bwa/${prefix}.amb + touch bwa/${prefix}.ann + touch bwa/${prefix}.bwt + touch bwa/${prefix}.pac + touch bwa/${prefix}.sa + """ +} diff --git a/modules/nf-core/bwa/index/meta.yml b/modules/nf-core/bwa/index/meta.yml new file mode 100644 index 0000000..f5bf7f5 --- /dev/null +++ b/modules/nf-core/bwa/index/meta.yml @@ -0,0 +1,71 @@ +name: bwa_index +description: Create BWA index for reference genome +keywords: + - index + - fasta + - genome + - reference +tools: + - bwa: + description: | + BWA is a software package for mapping DNA sequences against + a large reference genome, such as the human genome. + homepage: http://bio-bwa.sourceforge.net/ + documentation: https://bio-bwa.sourceforge.net/bwa.shtml + arxiv: arXiv:1303.3997 + licence: ["GPL-3.0-or-later"] + identifier: "biotools:bwa" +input: + - - meta: + type: map + description: | + Groovy Map containing reference information. + e.g. [ id:'test', single_end:false ] + - fasta: + type: file + description: Input genome fasta file + ontologies: + - edam: "http://edamontology.org/data_2044" # Sequence + - edam: "http://edamontology.org/format_1929" # FASTA +output: + index: + - - meta: + type: map + description: | + Groovy Map containing reference information. + e.g. [ id:'test', single_end:false ] + - bwa: + type: map + description: | + Groovy Map containing reference information. + e.g. [ id:'test', single_end:false ] + pattern: "*.{amb,ann,bwt,pac,sa}" + ontologies: + - edam: "http://edamontology.org/data_3210" # Genome index + versions_bwa: + - - ${task.process}: + type: string + description: The process the versions were collected from + - bwa: + type: string + description: The tool name + - 'bwa 2>&1 | sed -n "s/^Version: //p"': + type: string + description: The command used to generate the version of the tool +topics: + versions: + - - ${task.process}: + type: string + description: The process the versions were collected from + - bwa: + type: string + description: The tool name + - 'bwa 2>&1 | sed -n "s/^Version: //p"': + type: string + description: The command used to generate the version of the tool +authors: + - "@drpatelh" + - "@maxulysse" +maintainers: + - "@maxulysse" + - "@gallvp" diff --git a/modules/nf-core/bwa/index/tests/main.nf.test b/modules/nf-core/bwa/index/tests/main.nf.test new file mode 100644 index 0000000..f0fba82 --- /dev/null +++ b/modules/nf-core/bwa/index/tests/main.nf.test @@ -0,0 +1,57 @@ +nextflow_process { + + name "Test Process BWA_INDEX" + tag "modules_nfcore" + tag "modules" + tag "bwa" + tag "bwa/index" + script "../main.nf" + process "BWA_INDEX" + + test("BWA index") { + + when { + process { + """ + input[0] = [ + [id: 'test'], + file(params.modules_testdata_base_path + 'genomics/sarscov2/genome/genome.fasta', checkIfExists: true) + ] + """ + } + } + + then { + assert process.success + assertAll( + { assert snapshot(process.out).match() } + ) + } + + } + + test("BWA index - stub") { + + options "-stub" + + when { + process { + """ + input[0] = [ + [id: 'test'], + file(params.modules_testdata_base_path + 'genomics/sarscov2/genome/genome.fasta', checkIfExists: true) + ] + """ + } + } + + then { + assert process.success + assertAll( + { assert snapshot(process.out).match() } + ) + } + + } + +} diff --git a/modules/nf-core/bwa/index/tests/main.nf.test.snap b/modules/nf-core/bwa/index/tests/main.nf.test.snap new file mode 100644 index 0000000..21a6f73 --- /dev/null +++ b/modules/nf-core/bwa/index/tests/main.nf.test.snap @@ -0,0 +1,108 @@ +{ + "BWA index - stub": { + "content": [ + { + "0": [ + [ + { + "id": "test" + }, + [ + "genome.amb:md5,d41d8cd98f00b204e9800998ecf8427e", + "genome.ann:md5,d41d8cd98f00b204e9800998ecf8427e", + "genome.bwt:md5,d41d8cd98f00b204e9800998ecf8427e", + "genome.pac:md5,d41d8cd98f00b204e9800998ecf8427e", + "genome.sa:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ] + ], + "1": [ + [ + "BWA_INDEX", + "bwa", + "0.7.19-r1273" + ] + ], + "index": [ + [ + { + "id": "test" + }, + [ + "genome.amb:md5,d41d8cd98f00b204e9800998ecf8427e", + "genome.ann:md5,d41d8cd98f00b204e9800998ecf8427e", + "genome.bwt:md5,d41d8cd98f00b204e9800998ecf8427e", + "genome.pac:md5,d41d8cd98f00b204e9800998ecf8427e", + "genome.sa:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ] + ], + "versions_bwa": [ + [ + "BWA_INDEX", + "bwa", + "0.7.19-r1273" + ] + ] + } + ], + "meta": { + "nf-test": "0.9.3", + "nextflow": "25.10.2" + }, + "timestamp": "2026-01-23T16:58:59.966558606" + }, + "BWA index": { + "content": [ + { + "0": [ + [ + { + "id": "test" + }, + [ + "genome.amb:md5,3a68b8b2287e07dd3f5f95f4344ba76e", + "genome.ann:md5,c32e11f6c859f166c7525a9c1d583567", + "genome.bwt:md5,0469c30a1e239dd08f68afe66fde99da", + "genome.pac:md5,983e3d2cd6f36e2546e6d25a0da78d66", + "genome.sa:md5,ab3952cabf026b48cd3eb5bccbb636d1" + ] + ] + ], + "1": [ + [ + "BWA_INDEX", + "bwa", + "0.7.19-r1273" + ] + ], + "index": [ + [ + { + "id": "test" + }, + [ + "genome.amb:md5,3a68b8b2287e07dd3f5f95f4344ba76e", + "genome.ann:md5,c32e11f6c859f166c7525a9c1d583567", + "genome.bwt:md5,0469c30a1e239dd08f68afe66fde99da", + "genome.pac:md5,983e3d2cd6f36e2546e6d25a0da78d66", + "genome.sa:md5,ab3952cabf026b48cd3eb5bccbb636d1" + ] + ] + ], + "versions_bwa": [ + [ + "BWA_INDEX", + "bwa", + "0.7.19-r1273" + ] + ] + } + ], + "meta": { + "nf-test": "0.9.3", + "nextflow": "25.10.2" + }, + "timestamp": "2026-01-23T16:58:53.330725134" + } +} \ No newline at end of file diff --git a/modules/nf-core/bwa/mem/environment.yml b/modules/nf-core/bwa/mem/environment.yml new file mode 100644 index 0000000..54e6794 --- /dev/null +++ b/modules/nf-core/bwa/mem/environment.yml @@ -0,0 +1,13 @@ +--- +# yaml-language-server: $schema=https://raw.githubusercontent.com/nf-core/modules/master/modules/environment-schema.json +channels: + - conda-forge + - bioconda + +dependencies: + # renovate: datasource=conda depName=bioconda/bwa + - bioconda::bwa=0.7.19 + # renovate: datasource=conda depName=bioconda/htslib + - bioconda::htslib=1.22.1 + # renovate: datasource=conda depName=bioconda/samtools + - bioconda::samtools=1.22.1 diff --git a/modules/nf-core/bwa/mem/main.nf b/modules/nf-core/bwa/mem/main.nf new file mode 100644 index 0000000..bde6a9a --- /dev/null +++ b/modules/nf-core/bwa/mem/main.nf @@ -0,0 +1,73 @@ +process BWA_MEM { + tag "$meta.id" + label 'process_high' + + conda "${moduleDir}/environment.yml" + container "${ workflow.containerEngine in ['singularity', 'apptainer'] && !task.ext.singularity_pull_docker_container ? + 'https://community-cr-prod.seqera.io/docker/registry/v2/blobs/sha256/d7/d7e24dc1e4d93ca4d3a76a78d4c834a7be3985b0e1e56fddd61662e047863a8a/data' : + 'community.wave.seqera.io/library/bwa_htslib_samtools:83b50ff84ead50d0' }" + + input: + tuple val(meta) , path(reads) + tuple val(meta2), path(index) + tuple val(meta3), path(fasta) + val sort_bam + + output: + tuple val(meta), path("*.bam") , emit: bam, optional: true + tuple val(meta), path("*.cram") , emit: cram, optional: true + tuple val(meta), path("*.sam") , emit: sam, optional: true + tuple val(meta), path("*.csi") , emit: csi, optional: true + tuple val(meta), path("*.crai") , emit: crai, optional: true + tuple val("${task.process}"), val('bwa'), eval('bwa 2>&1 | sed -n "s/^Version: //p"'), topic: versions, emit: versions_bwa + tuple val("${task.process}"), val('samtools'), eval("samtools version | sed '1!d;s/.* //'"), topic: versions, emit: versions_samtools + + when: + task.ext.when == null || task.ext.when + + script: + def args = task.ext.args ?: '' + def args2 = task.ext.args2 ?: '' + def prefix = task.ext.prefix ?: "${meta.id}" + def samtools_command = sort_bam ? 'sort' : 'view' + def extension = args2.contains("--output-fmt sam") ? "sam" : + args2.contains("--output-fmt cram") ? "cram": + sort_bam && args2.contains("-O cram")? "cram": + !sort_bam && args2.contains("-C") ? "cram": + "bam" + def reference = fasta && extension=="cram" ? "--reference ${fasta}" : "" + if (!fasta && extension=="cram") error "Fasta reference is required for CRAM output" + // + // For SAM output we can skip samtools view + // + def pipe_command = "" + if (extension == "sam") { + pipe_command = "> ${prefix}.${extension}" + } else { + pipe_command = "| samtools $samtools_command $args2 ${reference} --threads $task.cpus -o ${prefix}.${extension} -" + } + """ + INDEX=`find -L ./ -name "*.amb" | sed 's/\\.amb\$//'` + + bwa mem \\ + $args \\ + -t $task.cpus \\ + \$INDEX \\ + $reads \\ + $pipe_command + """ + + stub: + def args2 = task.ext.args2 ?: '' + def prefix = task.ext.prefix ?: "${meta.id}" + def extension = args2.contains("--output-fmt sam") ? "sam" : + args2.contains("--output-fmt cram") ? "cram": + sort_bam && args2.contains("-O cram")? "cram": + !sort_bam && args2.contains("-C") ? "cram": + "bam" + """ + touch ${prefix}.${extension} + touch ${prefix}.csi + touch ${prefix}.crai + """ +} diff --git a/modules/nf-core/bwa/mem/meta.yml b/modules/nf-core/bwa/mem/meta.yml new file mode 100644 index 0000000..1c4ee88 --- /dev/null +++ b/modules/nf-core/bwa/mem/meta.yml @@ -0,0 +1,159 @@ +name: bwa_mem +description: Performs fastq alignment to a fasta reference using BWA +keywords: + - mem + - bwa + - alignment + - map + - fastq + - bam + - sam +tools: + - bwa: + description: | + BWA is a software package for mapping DNA sequences against + a large reference genome, such as the human genome. + homepage: http://bio-bwa.sourceforge.net/ + documentation: https://bio-bwa.sourceforge.net/bwa.shtml + arxiv: arXiv:1303.3997 + licence: + - "GPL-3.0-or-later" + identifier: "biotools:bwa" +input: + - - meta: + type: map + description: | + Groovy Map containing sample information + e.g. [ id:'test', single_end:false ] + - reads: + type: file + description: | + List of input FastQ files of size 1 and 2 for single-end and paired-end data, + respectively. + ontologies: + - edam: "http://edamontology.org/data_2044" + - edam: "http://edamontology.org/format_1930" + - - meta2: + type: map + description: | + Groovy Map containing reference information. + e.g. [ id:'test', single_end:false ] + - index: + type: file + description: BWA genome index files + pattern: "Directory containing BWA index *.{amb,ann,bwt,pac,sa}" + ontologies: + - edam: "http://edamontology.org/data_3210" + - - meta3: + type: map + description: | + Groovy Map containing sample information + e.g. [ id:'test', single_end:false ] + - fasta: + type: file + description: Reference genome in FASTA format + pattern: "*.{fasta,fa}" + ontologies: + - edam: "http://edamontology.org/data_2044" + - edam: "http://edamontology.org/format_1929" + - sort_bam: + type: boolean + description: use samtools sort (true) or samtools view (false) + pattern: "true or false" +output: + bam: + - - meta: + type: map + description: Groovy Map containing sample information + - "*.bam": + type: file + description: Output BAM file containing read alignments + pattern: "*.{bam}" + ontologies: + - edam: "http://edamontology.org/format_2572" + cram: + - - meta: + type: map + description: Groovy Map containing sample information + - "*.cram": + type: file + description: Output CRAM file containing read alignments + pattern: "*.{cram}" + ontologies: + - edam: "http://edamontology.org/format_3462" + sam: + - - meta: + type: map + description: Groovy Map containing sample information + - "*.sam": + type: file + description: Output SAM file containing read alignments + pattern: "*.{sam}" + ontologies: + - edam: "http://edamontology.org/format_2573" + csi: + - - meta: + type: map + description: Groovy Map containing sample information + - "*.csi": + type: file + description: Optional index file for BAM file + pattern: "*.{csi}" + ontologies: [] + crai: + - - meta: + type: map + description: Groovy Map containing sample information + - "*.crai": + type: file + description: Optional index file for CRAM file + pattern: "*.{crai}" + ontologies: [] + versions_bwa: + - - ${task.process}: + type: string + description: The name of the process + - bwa: + type: string + description: The name of the tool + - 'bwa 2>&1 | sed -n "s/^Version: //p"': + type: eval + description: The expression to obtain the version of the tool + versions_samtools: + - - ${task.process}: + type: string + description: The name of the process + - samtools: + type: string + description: The name of the tool + - samtools version | sed '1!d;s/.* //': + type: eval + description: The expression to obtain the version of the tool +topics: + versions: + - - ${task.process}: + type: string + description: The name of the process + - bwa: + type: string + description: The name of the tool + - 'bwa 2>&1 | sed -n "s/^Version: //p"': + type: eval + description: The expression to obtain the version of the tool + - - ${task.process}: + type: string + description: The name of the process + - samtools: + type: string + description: The name of the tool + - samtools version | sed '1!d;s/.* //': + type: eval + description: The expression to obtain the version of the tool +authors: + - "@drpatelh" + - "@jeremy1805" + - "@matthdsm" +maintainers: + - "@drpatelh" + - "@jeremy1805" + - "@matthdsm" diff --git a/modules/nf-core/bwa/mem/tests/main.nf.test b/modules/nf-core/bwa/mem/tests/main.nf.test new file mode 100644 index 0000000..e284e2e --- /dev/null +++ b/modules/nf-core/bwa/mem/tests/main.nf.test @@ -0,0 +1,343 @@ +nextflow_process { + + name "Test Process BWA_MEM" + tag "modules_nfcore" + tag "modules" + tag "bwa" + tag "bwa/mem" + tag "bwa/index" + script "../main.nf" + process "BWA_MEM" + + setup { + run("BWA_INDEX") { + script "../../index/main.nf" + process { + """ + input[0] = [ + [id: 'test'], + file(params.modules_testdata_base_path + 'genomics/sarscov2/genome/genome.fasta', checkIfExists: true) + ] + """ + } + } + } + + test("Single-End") { + + when { + process { + """ + input[0] = [ + [ id:'test', single_end:true ], // meta map + [ + file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/fastq/test_1.fastq.gz', checkIfExists: true) + ] + ] + input[1] = BWA_INDEX.out.index + input[2] = [[id: 'test'],file(params.modules_testdata_base_path + 'genomics/sarscov2/genome/genome.fasta', checkIfExists: true)] + input[3] = false + """ + } + } + + then { + assertAll( + { assert process.success }, + { assert snapshot( + process.out.cram, + process.out.sam, + process.out.csi, + process.out.crai, + process.out.findAll { key, val -> key.startsWith("versions") }, + bam(process.out.bam[0][1]).getReadsMD5() + ).match() + } + ) + } + + } + + test("Single-End Sort") { + + when { + process { + """ + input[0] = [ + [ id:'test', single_end:true ], // meta map + [ + file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/fastq/test_1.fastq.gz', checkIfExists: true) + ] + ] + input[1] = BWA_INDEX.out.index + input[2] = [[id: 'test'],file(params.modules_testdata_base_path + 'genomics/sarscov2/genome/genome.fasta', checkIfExists: true)] + input[3] = true + """ + } + } + + then { + assertAll( + { assert process.success }, + { assert snapshot( + process.out.cram, + process.out.sam, + process.out.csi, + process.out.crai, + process.out.findAll { key, val -> key.startsWith("versions") }, + bam(process.out.bam[0][1]).getReadsMD5() + ).match() + } + ) + } + + } + + test("Single-End - SAM output") { + + config "./nextflow_sam.config" + + when { + params { + module_args2 = "--output-fmt sam" + } + + process { + """ + input[0] = [ + [ id:'test', single_end:true ], // meta map + [ + file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/fastq/test_1.fastq.gz', checkIfExists: true) + ] + ] + input[1] = BWA_INDEX.out.index + input[2] = [[id: 'test'],file(params.modules_testdata_base_path + 'genomics/sarscov2/genome/genome.fasta', checkIfExists: true)] + input[3] = false + """ + } + } + + then { + assertAll( + { assert process.success }, + { assert snapshot( + process.out.bam, + process.out.cram, + process.out.csi, + process.out.crai, + process.out.findAll { key, val -> key.startsWith("versions") }, + bam(process.out.sam[0][1]).getReadsMD5() + ).match() + } + ) + } + + } + + test("Paired-End") { + + when { + process { + """ + input[0] = [ + [ id:'test', single_end:false ], // meta map + [ + file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/fastq/test_1.fastq.gz', checkIfExists: true), + file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/fastq/test_2.fastq.gz', checkIfExists: true) + ] + ] + input[1] = BWA_INDEX.out.index + input[2] = [[id: 'test'],file(params.modules_testdata_base_path + 'genomics/sarscov2/genome/genome.fasta', checkIfExists: true)] + input[3] = false + """ + } + } + + then { + assertAll( + { assert process.success }, + { assert snapshot( + process.out.cram, + process.out.sam, + process.out.csi, + process.out.crai, + process.out.findAll { key, val -> key.startsWith("versions") }, + bam(process.out.bam[0][1]).getReadsMD5() + ).match() + } + ) + } + + } + + test("Paired-End Sort") { + + when { + process { + """ + input[0] = [ + [ id:'test', single_end:false ], // meta map + [ + file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/fastq/test_1.fastq.gz', checkIfExists: true), + file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/fastq/test_2.fastq.gz', checkIfExists: true) + ] + ] + input[1] = BWA_INDEX.out.index + input[2] = [[id: 'test'],file(params.modules_testdata_base_path + 'genomics/sarscov2/genome/genome.fasta', checkIfExists: true)] + input[3] = true + """ + } + } + + then { + assertAll( + { assert process.success }, + { assert snapshot( + process.out.cram, + process.out.sam, + process.out.csi, + process.out.crai, + process.out.findAll { key, val -> key.startsWith("versions") }, + bam(process.out.bam[0][1]).getReadsMD5() + ).match() + } + ) + } + + } + + test("Paired-End - no fasta") { + + when { + process { + """ + input[0] = [ + [ id:'test', single_end:false ], // meta map + [ + file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/fastq/test_1.fastq.gz', checkIfExists: true), + file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/fastq/test_2.fastq.gz', checkIfExists: true) + ] + ] + input[1] = BWA_INDEX.out.index + input[2] = [[:],[]] + input[3] = false + """ + } + } + + then { + assertAll( + { assert process.success }, + { assert snapshot( + process.out.cram, + process.out.sam, + process.out.csi, + process.out.crai, + process.out.findAll { key, val -> key.startsWith("versions") }, + bam(process.out.bam[0][1]).getReadsMD5() + ).match() + } + ) + } + + } + + test ("Paired-end - SAM output") { + + config "./nextflow_sam.config" + + when { + params { + module_args2 = "--output-fmt sam" + } + + process { + """ + input[0] = [ + [ id:'test', single_end:false ], // meta map + [ + file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/fastq/test_1.fastq.gz', checkIfExists: true), + file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/fastq/test_2.fastq.gz', checkIfExists: true) + ] + ] + input[1] = BWA_INDEX.out.index + input[2] = [[:],[]] + input[3] = false + """ + } + } + + then { + assertAll( + { assert process.success }, + { assert snapshot( + process.out.bam, + process.out.cram, + process.out.csi, + process.out.crai, + process.out.findAll { key, val -> key.startsWith("versions") }, + bam(process.out.sam[0][1]).getReadsMD5() + ).match() + } + ) + } + + } + + test("Single-end - stub") { + + options "-stub" + + when { + process { + """ + input[0] = [ + [ id:'test', single_end:true ], // meta map + [ + file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/fastq/test_1.fastq.gz', checkIfExists: true) + ] + ] + input[1] = BWA_INDEX.out.index + input[2] = [[id: 'test'],file(params.modules_testdata_base_path + 'genomics/sarscov2/genome/genome.fasta', checkIfExists: true)] + input[3] = false + """ + } + } + + then { + assertAll( + { assert process.success }, + { assert snapshot(process.out).match() } + ) + } + } + + test("Paired-end - stub") { + + options "-stub" + + when { + process { + """ + input[0] = [ + [ id:'test', single_end:false ], // meta map + [ + file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/fastq/test_1.fastq.gz', checkIfExists: true), + file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/fastq/test_2.fastq.gz', checkIfExists: true) + ] + ] + input[1] = BWA_INDEX.out.index + input[2] = [[id: 'test'],file(params.modules_testdata_base_path + 'genomics/sarscov2/genome/genome.fasta', checkIfExists: true)] + input[3] = false + """ + } + } + + then { + assertAll( + { assert process.success }, + { assert snapshot(process.out).match() } + ) + } + } +} diff --git a/modules/nf-core/bwa/mem/tests/main.nf.test.snap b/modules/nf-core/bwa/mem/tests/main.nf.test.snap new file mode 100644 index 0000000..6f9031d --- /dev/null +++ b/modules/nf-core/bwa/mem/tests/main.nf.test.snap @@ -0,0 +1,478 @@ +{ + "Single-End - SAM output": { + "content": [ + [ + + ], + [ + + ], + [ + + ], + [ + + ], + { + "versions_bwa": [ + [ + "BWA_MEM", + "bwa", + "0.7.19-r1273" + ] + ], + "versions_samtools": [ + [ + "BWA_MEM", + "samtools", + "1.22.1" + ] + ] + }, + "798439cbd7fd81cbcc5078022dc5479d" + ], + "timestamp": "2026-05-11T12:09:32.334359515", + "meta": { + "nf-test": "0.9.5", + "nextflow": "26.04.0" + } + }, + "Single-End": { + "content": [ + [ + + ], + [ + + ], + [ + + ], + [ + + ], + { + "versions_bwa": [ + [ + "BWA_MEM", + "bwa", + "0.7.19-r1273" + ] + ], + "versions_samtools": [ + [ + "BWA_MEM", + "samtools", + "1.22.1" + ] + ] + }, + "798439cbd7fd81cbcc5078022dc5479d" + ], + "timestamp": "2026-05-11T12:07:21.233636979", + "meta": { + "nf-test": "0.9.5", + "nextflow": "26.04.0" + } + }, + "Single-End Sort": { + "content": [ + [ + + ], + [ + + ], + [ + + ], + [ + + ], + { + "versions_bwa": [ + [ + "BWA_MEM", + "bwa", + "0.7.19-r1273" + ] + ], + "versions_samtools": [ + [ + "BWA_MEM", + "samtools", + "1.22.1" + ] + ] + }, + "94fcf617f5b994584c4e8d4044e16b4f" + ], + "timestamp": "2026-05-11T12:07:28.74614221", + "meta": { + "nf-test": "0.9.5", + "nextflow": "26.04.0" + } + }, + "Paired-End": { + "content": [ + [ + + ], + [ + + ], + [ + + ], + [ + + ], + { + "versions_bwa": [ + [ + "BWA_MEM", + "bwa", + "0.7.19-r1273" + ] + ], + "versions_samtools": [ + [ + "BWA_MEM", + "samtools", + "1.22.1" + ] + ] + }, + "57aeef88ed701a8ebc8e2f0a381b2a6" + ], + "timestamp": "2026-05-11T12:07:42.612131595", + "meta": { + "nf-test": "0.9.5", + "nextflow": "26.04.0" + } + }, + "Paired-End Sort": { + "content": [ + [ + + ], + [ + + ], + [ + + ], + [ + + ], + { + "versions_bwa": [ + [ + "BWA_MEM", + "bwa", + "0.7.19-r1273" + ] + ], + "versions_samtools": [ + [ + "BWA_MEM", + "samtools", + "1.22.1" + ] + ] + }, + "af8628d9df18b2d3d4f6fd47ef2bb872" + ], + "timestamp": "2026-05-11T12:09:45.938323098", + "meta": { + "nf-test": "0.9.5", + "nextflow": "26.04.0" + } + }, + "Single-end - stub": { + "content": [ + { + "0": [ + [ + { + "id": "test", + "single_end": true + }, + "test.bam:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "1": [ + + ], + "2": [ + + ], + "3": [ + [ + { + "id": "test", + "single_end": true + }, + "test.csi:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "4": [ + [ + { + "id": "test", + "single_end": true + }, + "test.crai:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "5": [ + [ + "BWA_MEM", + "bwa", + "0.7.19-r1273" + ] + ], + "6": [ + [ + "BWA_MEM", + "samtools", + "1.22.1" + ] + ], + "bam": [ + [ + { + "id": "test", + "single_end": true + }, + "test.bam:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "crai": [ + [ + { + "id": "test", + "single_end": true + }, + "test.crai:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "cram": [ + + ], + "csi": [ + [ + { + "id": "test", + "single_end": true + }, + "test.csi:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "sam": [ + + ], + "versions_bwa": [ + [ + "BWA_MEM", + "bwa", + "0.7.19-r1273" + ] + ], + "versions_samtools": [ + [ + "BWA_MEM", + "samtools", + "1.22.1" + ] + ] + } + ], + "timestamp": "2026-05-11T12:10:09.92486753", + "meta": { + "nf-test": "0.9.5", + "nextflow": "26.04.0" + } + }, + "Paired-End - no fasta": { + "content": [ + [ + + ], + [ + + ], + [ + + ], + [ + + ], + { + "versions_bwa": [ + [ + "BWA_MEM", + "bwa", + "0.7.19-r1273" + ] + ], + "versions_samtools": [ + [ + "BWA_MEM", + "samtools", + "1.22.1" + ] + ] + }, + "57aeef88ed701a8ebc8e2f0a381b2a6" + ], + "timestamp": "2026-05-11T12:09:52.820539909", + "meta": { + "nf-test": "0.9.5", + "nextflow": "26.04.0" + } + }, + "Paired-end - SAM output": { + "content": [ + [ + + ], + [ + + ], + [ + + ], + [ + + ], + { + "versions_bwa": [ + [ + "BWA_MEM", + "bwa", + "0.7.19-r1273" + ] + ], + "versions_samtools": [ + [ + "BWA_MEM", + "samtools", + "1.22.1" + ] + ] + }, + "57aeef88ed701a8ebc8e2f0a381b2a6" + ], + "timestamp": "2026-05-11T12:10:00.199968933", + "meta": { + "nf-test": "0.9.5", + "nextflow": "26.04.0" + } + }, + "Paired-end - stub": { + "content": [ + { + "0": [ + [ + { + "id": "test", + "single_end": false + }, + "test.bam:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "1": [ + + ], + "2": [ + + ], + "3": [ + [ + { + "id": "test", + "single_end": false + }, + "test.csi:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "4": [ + [ + { + "id": "test", + "single_end": false + }, + "test.crai:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "5": [ + [ + "BWA_MEM", + "bwa", + "0.7.19-r1273" + ] + ], + "6": [ + [ + "BWA_MEM", + "samtools", + "1.22.1" + ] + ], + "bam": [ + [ + { + "id": "test", + "single_end": false + }, + "test.bam:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "crai": [ + [ + { + "id": "test", + "single_end": false + }, + "test.crai:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "cram": [ + + ], + "csi": [ + [ + { + "id": "test", + "single_end": false + }, + "test.csi:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "sam": [ + + ], + "versions_bwa": [ + [ + "BWA_MEM", + "bwa", + "0.7.19-r1273" + ] + ], + "versions_samtools": [ + [ + "BWA_MEM", + "samtools", + "1.22.1" + ] + ] + } + ], + "timestamp": "2026-05-11T12:10:16.940291647", + "meta": { + "nf-test": "0.9.5", + "nextflow": "26.04.0" + } + } +} \ No newline at end of file diff --git a/modules/nf-core/bwa/mem/tests/nextflow_sam.config b/modules/nf-core/bwa/mem/tests/nextflow_sam.config new file mode 100644 index 0000000..831332e --- /dev/null +++ b/modules/nf-core/bwa/mem/tests/nextflow_sam.config @@ -0,0 +1,5 @@ +process { + withName: BWA_MEM { + ext.args2 = params.module_args2 + } +} diff --git a/modules/nf-core/multiqc/.conda-lock/linux_amd64-bd-c1f4a7982b743963_1.txt b/modules/nf-core/multiqc/.conda-lock/linux_amd64-bd-c17fb751507e9dfc_1.txt similarity index 75% rename from modules/nf-core/multiqc/.conda-lock/linux_amd64-bd-c1f4a7982b743963_1.txt rename to modules/nf-core/multiqc/.conda-lock/linux_amd64-bd-c17fb751507e9dfc_1.txt index 7619030..2a91c22 100644 --- a/modules/nf-core/multiqc/.conda-lock/linux_amd64-bd-c1f4a7982b743963_1.txt +++ b/modules/nf-core/multiqc/.conda-lock/linux_amd64-bd-c17fb751507e9dfc_1.txt @@ -14,120 +14,118 @@ linux-64: - conda: https://conda.anaconda.org/conda-forge/noarch/_python_abi3_support-1.0-hd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.7.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/attrs-26.1.0-pyhcf101f3_0.conda -- conda: https://conda.anaconda.org/conda-forge/noarch/backports.zstd-1.3.0-py314h680f03e_0.conda +- conda: https://conda.anaconda.org/conda-forge/noarch/backports.zstd-1.5.0-py314h680f03e_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-python-1.2.0-py314h3de4e8d_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-hda65f42_9.conda -- conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.2.25-hbd8a1cb_0.conda -- conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2026.2.25-pyhd8ed1ab_0.conda -- conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.6-pyhd8ed1ab_0.conda -- conda: https://conda.anaconda.org/conda-forge/noarch/click-8.3.1-pyh8f84b5b_1.conda +- conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.5.20-hbd8a1cb_0.conda +- conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2026.5.20-pyhd8ed1ab_0.conda +- conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.7-pyhd8ed1ab_0.conda +- conda: https://conda.anaconda.org/conda-forge/noarch/click-8.4.0-pyhc90fa1f_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/coloredlogs-15.0.1-pyhd8ed1ab_4.conda - conda: https://conda.anaconda.org/conda-forge/noarch/colormath-3.0.0-pyhd8ed1ab_4.conda -- conda: https://conda.anaconda.org/conda-forge/noarch/cpython-3.14.3-py314hd8ed1ab_101.conda -- conda: https://conda.anaconda.org/conda-forge/linux-64/expat-2.7.4-hecca717_0.conda +- conda: https://conda.anaconda.org/conda-forge/noarch/cpython-3.14.5-py314hd8ed1ab_100.conda +- conda: https://conda.anaconda.org/conda-forge/linux-64/expat-2.8.1-hecca717_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-dejavu-sans-mono-2.37-hab24e00_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-inconsolata-3.000-h77eed37_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-source-code-pro-2.038-h77eed37_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-ubuntu-0.83-h77eed37_3.conda -- conda: https://conda.anaconda.org/conda-forge/linux-64/fontconfig-2.17.1-h27c8c51_0.conda +- conda: https://conda.anaconda.org/conda-forge/linux-64/fontconfig-2.18.0-h27c8c51_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/fonts-conda-forge-1-hc364b38_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.3.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/hpack-4.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/humanfriendly-10.0-pyh707e725_8.conda - conda: https://conda.anaconda.org/conda-forge/noarch/humanize-4.15.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.1.0-pyhd8ed1ab_0.conda -- conda: https://conda.anaconda.org/conda-forge/linux-64/icu-78.3-h33c6efd_0.conda -- conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.11-pyhd8ed1ab_0.conda -- conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.8.0-pyhcf101f3_0.conda +- conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.15-pyhcf101f3_0.conda +- conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-9.0.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.6-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jsonschema-4.26.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jsonschema-specifications-2025.9.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/kaleido-core-0.2.1-h3644ca4_0.tar.bz2 -- conda: https://conda.anaconda.org/conda-forge/linux-64/lcms2-2.18-h0c24ade_0.conda +- conda: https://conda.anaconda.org/conda-forge/linux-64/lcms2-2.19.1-h0c24ade_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.45.1-default_hbd61a6d_102.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/lerc-4.1.0-hdb68285_0.conda -- conda: https://conda.anaconda.org/conda-forge/linux-64/libblas-3.11.0-5_h4a7cf45_openblas.conda -- conda: https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.11.0-5_h0358290_openblas.conda +- conda: https://conda.anaconda.org/conda-forge/linux-64/libblas-3.11.0-7_h4a7cf45_openblas.conda +- conda: https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.11.0-7_h0358290_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libdeflate-1.25-h17f619e_0.conda -- conda: https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.7.4-hecca717_0.conda +- conda: https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.8.1-hecca717_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.5.2-h3435931_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libfreetype-2.14.3-ha770c72_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libfreetype6-2.14.3-h73754d4_0.conda -- conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-15.2.0-he0feb66_18.conda -- conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-15.2.0-h69a702a_18.conda -- conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-15.2.0-h69a702a_18.conda -- conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-15.2.0-h68bc16d_18.conda -- conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-15.2.0-he0feb66_18.conda -- conda: https://conda.anaconda.org/conda-forge/linux-64/libjpeg-turbo-3.1.2-hb03c661_0.conda -- conda: https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.11.0-5_h47877c9_openblas.conda -- conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.2-hb03c661_0.conda +- conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-15.2.0-he0feb66_19.conda +- conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-15.2.0-h69a702a_19.conda +- conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-15.2.0-h69a702a_19.conda +- conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-15.2.0-h68bc16d_19.conda +- conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-15.2.0-he0feb66_19.conda +- conda: https://conda.anaconda.org/conda-forge/linux-64/libjpeg-turbo-3.1.4.1-hb03c661_0.conda +- conda: https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.11.0-7_h47877c9_openblas.conda +- conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.3-hb03c661_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libmpdec-4.0.0-hb03c661_1.conda -- conda: https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.30-pthreads_h94d23a6_4.conda -- conda: https://conda.anaconda.org/conda-forge/linux-64/libpng-1.6.55-h421ea60_0.conda -- conda: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.52.0-hf4e2dac_0.conda -- conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-15.2.0-h934c35e_18.conda +- conda: https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.33-pthreads_h94d23a6_0.conda +- conda: https://conda.anaconda.org/conda-forge/linux-64/libpng-1.6.58-h421ea60_0.conda +- conda: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.53.1-h0c1763c_0.conda +- conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-15.2.0-h934c35e_19.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.7.1-h9d88235_1.conda -- conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.41.3-h5347b49_0.conda +- conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.42.1-h5347b49_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libwebp-base-1.6.0-hd42ef1d_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libxcb-1.17.0-h8a09558_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.2-h25fd6f3_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-3.10.2-pyhcf101f3_0.conda -- conda: https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-4.0.0-pyhd8ed1ab_0.conda +- conda: https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-4.2.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/markupsafe-3.0.3-py314h67df5f8_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/mathjax-2.7.7-ha770c72_3.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/mdurl-0.1.2-pyhd8ed1ab_1.conda -- conda: https://conda.anaconda.org/bioconda/noarch/multiqc-1.33-pyhdfd78af_0.conda -- conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-2.18.1-pyhcf101f3_1.conda +- conda: https://conda.anaconda.org/bioconda/noarch/multiqc-1.35-pyhdfd78af_1.conda +- conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-2.21.2-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/natsort-8.4.0-pyhcf101f3_2.conda -- conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.5-h2d0b736_3.conda +- conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.6-hdb14827_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/networkx-3.6.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/nspr-4.38-h29cc59b_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/nss-3.118-h445c969_0.conda -- conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.4.3-py314h2b28147_0.conda +- conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.4.6-py314h2b28147_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/openjpeg-2.5.4-h55fea9a_0.conda -- conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.6.1-h35e630c_1.conda -- conda: https://conda.anaconda.org/conda-forge/noarch/packaging-26.0-pyhcf101f3_0.conda -- conda: https://conda.anaconda.org/conda-forge/linux-64/pillow-12.1.1-py314h8ec4b1a_0.conda +- conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.6.2-h35e630c_0.conda +- conda: https://conda.anaconda.org/conda-forge/noarch/packaging-26.2-pyhc364b38_0.conda +- conda: https://conda.anaconda.org/conda-forge/linux-64/pillow-12.2.0-py314h8ec4b1a_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/plotly-6.6.0-pyhd8ed1ab_0.conda -- conda: https://conda.anaconda.org/conda-forge/noarch/polars-1.39.3-pyh58ad624_1.conda -- conda: https://conda.anaconda.org/conda-forge/noarch/polars-lts-cpu-1.34.0.deprecated-hc364b38_0.conda -- conda: https://conda.anaconda.org/conda-forge/linux-64/polars-runtime-32-1.39.3-py310hffdcd12_1.conda -- conda: https://conda.anaconda.org/conda-forge/linux-64/polars-runtime-compat-1.39.3-py310hbcd5346_1.conda +- conda: https://conda.anaconda.org/conda-forge/noarch/polars-1.41.0-pyh58ad624_0.conda +- conda: https://conda.anaconda.org/conda-forge/linux-64/polars-runtime-32-1.41.0-py310h49dadd8_0.conda +- conda: https://conda.anaconda.org/conda-forge/linux-64/polars-runtime-compat-1.41.0-py310hcbd6021_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/procps-ng-4.0.6-h18c060e_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pthread-stubs-0.4-hb9d3cd8_1002.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pyaml-env-1.2.2-pyhd8ed1ab_0.conda -- conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-2.12.5-pyhcf101f3_1.conda -- conda: https://conda.anaconda.org/conda-forge/linux-64/pydantic-core-2.41.5-py314h2e6c369_1.conda -- conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.19.2-pyhd8ed1ab_0.conda +- conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-2.13.4-pyhcf101f3_0.conda +- conda: https://conda.anaconda.org/conda-forge/linux-64/pydantic-core-2.46.4-py314h2e6c369_0.conda +- conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.20.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda -- conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.14.3-h32b2ec7_101_cp314.conda +- conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.14.5-habeac84_100_cp314.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-dotenv-1.2.2-pyhcf101f3_0.conda -- conda: https://conda.anaconda.org/conda-forge/noarch/python-gil-3.14.3-h4df99d1_101.conda +- conda: https://conda.anaconda.org/conda-forge/noarch/python-gil-3.14.5-h4df99d1_100.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-kaleido-0.2.1-pyhd8ed1ab_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.14-8_cp314.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pyyaml-6.0.3-py314h67df5f8_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/readline-8.3-h853b02a_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/referencing-0.37.0-pyhcf101f3_0.conda -- conda: https://conda.anaconda.org/conda-forge/linux-64/regex-2026.2.28-py314h5bd0f2a_0.conda -- conda: https://conda.anaconda.org/conda-forge/noarch/requests-2.32.5-pyhcf101f3_1.conda -- conda: https://conda.anaconda.org/conda-forge/noarch/rich-14.3.3-pyhcf101f3_0.conda +- conda: https://conda.anaconda.org/conda-forge/linux-64/regex-2026.5.9-py314h5bd0f2a_0.conda +- conda: https://conda.anaconda.org/conda-forge/noarch/requests-2.34.2-pyhcf101f3_0.conda +- conda: https://conda.anaconda.org/conda-forge/noarch/rich-15.0.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/rich-click-1.9.7-pyh8f84b5b_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/rpds-py-0.30.0-py314h2e6c369_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/spectra-0.0.11-pyhd8ed1ab_2.conda -- conda: https://conda.anaconda.org/conda-forge/linux-64/sqlite-3.52.0-h04a0ce9_0.conda +- conda: https://conda.anaconda.org/conda-forge/linux-64/sqlite-3.53.1-hbc0de68_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/tiktoken-0.12.0-py314h67fec18_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_h366c992_103.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tqdm-4.67.3-pyh8f84b5b_0.conda -- conda: https://conda.anaconda.org/conda-forge/noarch/typeguard-4.5.1-pyhd8ed1ab_0.conda +- conda: https://conda.anaconda.org/conda-forge/noarch/typeguard-4.5.2-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.15.0-h396c80c_0.conda -- conda: https://conda.anaconda.org/conda-forge/noarch/typing-inspection-0.4.2-pyhd8ed1ab_1.conda +- conda: https://conda.anaconda.org/conda-forge/noarch/typing-inspection-0.4.2-pyhcf101f3_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025c-hc9c84f9_1.conda -- conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.6.3-pyhd8ed1ab_0.conda +- conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.7.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxau-1.0.12-hb03c661_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxdmcp-1.1.5-hb03c661_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/yaml-0.2.5-h280c20c_3.conda -- conda: https://conda.anaconda.org/conda-forge/noarch/zipp-3.23.0-pyhcf101f3_1.conda +- conda: https://conda.anaconda.org/conda-forge/noarch/zipp-4.1.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/zlib-ng-2.3.3-hceb46e0_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb78ec9c_6.conda packages: @@ -174,15 +172,15 @@ license: MIT license_family: MIT size: 64927 timestamp: 1773935801332 -- conda: https://conda.anaconda.org/conda-forge/noarch/backports.zstd-1.3.0-py314h680f03e_0.conda +- conda: https://conda.anaconda.org/conda-forge/noarch/backports.zstd-1.5.0-py314h680f03e_0.conda noarch: generic -sha256: c31ab719d256bc6f89926131e88ecd0f0c5d003fe8481852c6424f4ec6c7eb29 -md5: a2ac7763a9ac75055b68f325d3255265 +sha256: a1c97297e867776760489537bc5ae36fa83a154be30e3b79385a39ca4cb058fe +md5: 1133126d840e75287d83947be3fc3e71 depends: - python >=3.14 license: BSD-3-Clause AND MIT AND EPL-2.0 -size: 7514 -timestamp: 1767044983590 +size: 7533 +timestamp: 1778594057496 - conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-python-1.2.0-py314h3de4e8d_1.conda sha256: 3ad3500bff54a781c29f16ce1b288b36606e2189d0b0ef2f67036554f47f12b0 md5: 8910d2c46f7e7b519129f486e0fe927a @@ -208,42 +206,42 @@ license: bzip2-1.0.6 license_family: BSD size: 260182 timestamp: 1771350215188 -- conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.2.25-hbd8a1cb_0.conda -sha256: 67cc7101b36421c5913a1687ef1b99f85b5d6868da3abbf6ec1a4181e79782fc -md5: 4492fd26db29495f0ba23f146cd5638d +- conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.5.20-hbd8a1cb_0.conda +sha256: 9812a303a1395e1dafbd92e5bc8a1ff6013bcbba0a09c7f03a8d23e43560aa9b +md5: 489b8e97e666c93f68fdb35c3c9b957f depends: - __unix license: ISC -size: 147413 -timestamp: 1772006283803 -- conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2026.2.25-pyhd8ed1ab_0.conda -sha256: a6b118fd1ed6099dc4fc03f9c492b88882a780fadaef4ed4f93dc70757713656 -md5: 765c4d97e877cdbbb88ff33152b86125 +size: 129868 +timestamp: 1779289852439 +- conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2026.5.20-pyhd8ed1ab_0.conda +sha256: 645655a3510e38e625da136595f3f16f2130c3263630cc3bc8f60f619ddbe490 +md5: 9fefff2f745ea1cc2ef15211a20c054a depends: - python >=3.10 license: ISC -size: 151445 -timestamp: 1772001170301 -- conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.6-pyhd8ed1ab_0.conda -sha256: d86dfd428b2e3c364fa90e07437c8405d635aa4ef54b25ab51d9c712be4112a5 -md5: 49ee13eb9b8f44d63879c69b8a40a74b +size: 134201 +timestamp: 1779285131141 +- conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.7-pyhd8ed1ab_0.conda +sha256: 3f9483d62ce24ecd063f8a5a714448445dc8d9e201147c46699fc0033e824457 +md5: a9167b9571f3baa9d448faa2139d1089 depends: - python >=3.10 license: MIT license_family: MIT -size: 58510 -timestamp: 1773660086450 -- conda: https://conda.anaconda.org/conda-forge/noarch/click-8.3.1-pyh8f84b5b_1.conda -sha256: 38cfe1ee75b21a8361c8824f5544c3866f303af1762693a178266d7f198e8715 -md5: ea8a6c3256897cc31263de9f455e25d9 +size: 58872 +timestamp: 1775127203018 +- conda: https://conda.anaconda.org/conda-forge/noarch/click-8.4.0-pyhc90fa1f_0.conda +sha256: 99ab8ef815c4520cce3a7482c2513f377c14348206857661d84c76a55e030f97 +md5: 003767c47f1f0a474c4de268b57839c3 depends: -- python >=3.10 - __unix - python +- python >=3.10 license: BSD-3-Clause license_family: BSD -size: 97676 -timestamp: 1764518652276 +size: 104631 +timestamp: 1779108494556 - conda: https://conda.anaconda.org/conda-forge/noarch/coloredlogs-15.0.1-pyhd8ed1ab_4.conda sha256: 8021c76eeadbdd5784b881b165242db9449783e12ce26d6234060026fd6a8680 md5: b866ff7007b934d564961066c8195983 @@ -265,27 +263,27 @@ license: BSD-3-Clause license_family: BSD size: 39326 timestamp: 1735759976140 -- conda: https://conda.anaconda.org/conda-forge/noarch/cpython-3.14.3-py314hd8ed1ab_101.conda +- conda: https://conda.anaconda.org/conda-forge/noarch/cpython-3.14.5-py314hd8ed1ab_100.conda noarch: generic -sha256: 91b06300879df746214f7363d6c27c2489c80732e46a369eb2afc234bcafb44c -md5: 3bb89e4f795e5414addaa531d6b1500a +sha256: 777882d2685f368417f31bbe1b28f73687fc6c8f6a5768bda20ffeefa6b07f5b +md5: a749029ce5d0632a913db19d17f944ab depends: - python >=3.14,<3.15.0a0 - python_abi * *_cp314 license: Python-2.0 -size: 50078 -timestamp: 1770674447292 -- conda: https://conda.anaconda.org/conda-forge/linux-64/expat-2.7.4-hecca717_0.conda -sha256: 0cc345e4dead417996ce9a1f088b28d858f03d113d43c1963d29194366dcce27 -md5: a0535741a4934b3e386051065c58761a +size: 50212 +timestamp: 1779236682725 +- conda: https://conda.anaconda.org/conda-forge/linux-64/expat-2.8.1-hecca717_0.conda +sha256: 29a10599d56d93bd750914888ebe6822d47722070762b4647b34d12df9f4476e +md5: d0757fd84af06f065eba49d39af6c546 depends: - __glibc >=2.17,<3.0.a0 -- libexpat 2.7.4 hecca717_0 +- libexpat 2.8.1 hecca717_0 - libgcc >=14 license: MIT license_family: MIT -size: 145274 -timestamp: 1771259434699 +size: 148238 +timestamp: 1779278694477 - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-dejavu-sans-mono-2.37-hab24e00_0.tar.bz2 sha256: 58d7f40d2940dd0a8aa28651239adbf5613254df0f75789919c4e6762054403b md5: 0c96522c6bdaed4b1566d11387caaf45 @@ -314,21 +312,21 @@ license: LicenseRef-Ubuntu-Font-Licence-Version-1.0 license_family: Other size: 1620504 timestamp: 1727511233259 -- conda: https://conda.anaconda.org/conda-forge/linux-64/fontconfig-2.17.1-h27c8c51_0.conda -sha256: aa4a44dba97151221100a637c7f4bde619567afade9c0265f8e1c8eed8d7bd8c -md5: 867127763fbe935bab59815b6e0b7b5c +- conda: https://conda.anaconda.org/conda-forge/linux-64/fontconfig-2.18.0-h27c8c51_0.conda +sha256: e798086d8a65d55dc4c51f5746705639c9a5f2eeb0b8fc50e6152cfc0d69a4e8 +md5: 06965b2f9854d0b15e0443ee81fe83dc depends: - __glibc >=2.17,<3.0.a0 -- libexpat >=2.7.4,<3.0a0 -- libfreetype >=2.14.1 -- libfreetype6 >=2.14.1 +- libexpat >=2.8.1,<3.0a0 +- libfreetype >=2.14.3 +- libfreetype6 >=2.14.3 - libgcc >=14 -- libuuid >=2.41.3,<3.0a0 -- libzlib >=1.3.1,<2.0a0 +- libuuid >=2.42.1,<3.0a0 +- libzlib >=1.3.2,<2.0a0 license: MIT license_family: MIT -size: 270705 -timestamp: 1771382710863 +size: 280882 +timestamp: 1779421631622 - conda: https://conda.anaconda.org/conda-forge/noarch/fonts-conda-forge-1-hc364b38_1.conda sha256: 54eea8469786bc2291cc40bca5f46438d3e062a399e8f53f013b6a9f50e98333 md5: a7970cd949a077b7cb9696379d338681 @@ -390,37 +388,26 @@ license: MIT license_family: MIT size: 17397 timestamp: 1737618427549 -- conda: https://conda.anaconda.org/conda-forge/linux-64/icu-78.3-h33c6efd_0.conda -sha256: fbf86c4a59c2ed05bbffb2ba25c7ed94f6185ec30ecb691615d42342baa1a16a -md5: c80d8a3b84358cb967fa81e7075fbc8a -depends: -- __glibc >=2.17,<3.0.a0 -- libgcc >=14 -- libstdcxx >=14 -license: MIT -license_family: MIT -size: 12723451 -timestamp: 1773822285671 -- conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.11-pyhd8ed1ab_0.conda -sha256: ae89d0299ada2a3162c2614a9d26557a92aa6a77120ce142f8e0109bbf0342b0 -md5: 53abe63df7e10a6ba605dc5f9f961d36 +- conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.15-pyhcf101f3_0.conda +sha256: 3d25f9f6f7ab3e1ce6429fc8c8aae0335cf446692e715068488536d220cc43de +md5: 1b9083b7f00609605d1483dbc6071a81 depends: - python >=3.10 +- python license: BSD-3-Clause license_family: BSD -size: 50721 -timestamp: 1760286526795 -- conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.8.0-pyhcf101f3_0.conda -sha256: 82ab2a0d91ca1e7e63ab6a4939356667ef683905dea631bc2121aa534d347b16 -md5: 080594bf4493e6bae2607e65390c520a +size: 62642 +timestamp: 1779294335905 +- conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-9.0.0-pyhcf101f3_0.conda +sha256: 43e2a5497cad1598ff88a3e69f69bc88b7b8f141fa63c60eab5db296317318b8 +md5: ffc17e785d64e12fc311af9184221839 depends: - python >=3.10 - zipp >=3.20 - python license: Apache-2.0 -license_family: APACHE -size: 34387 -timestamp: 1773931568510 +size: 34766 +timestamp: 1779714582554 - conda: https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.6-pyhcf101f3_1.conda sha256: fc9ca7348a4f25fed2079f2153ecdcf5f9cf2a0bc36c4172420ca09e1849df7b md5: 04558c96691bed63104678757beb4f8d @@ -474,18 +461,18 @@ license: MIT license_family: MIT size: 62099926 timestamp: 1615199463039 -- conda: https://conda.anaconda.org/conda-forge/linux-64/lcms2-2.18-h0c24ade_0.conda -sha256: 836ec4b895352110335b9fdcfa83a8dcdbe6c5fb7c06c4929130600caea91c0a -md5: 6f2e2c8f58160147c4d1c6f4c14cbac4 +- conda: https://conda.anaconda.org/conda-forge/linux-64/lcms2-2.19.1-h0c24ade_0.conda +sha256: eb89c6c39f2f6a93db55723dbb2f6bba8c8e63e6312bf1abf13e6e9ff45849c8 +md5: f92f984b558e6e6204014b16d212b271 depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 -- libjpeg-turbo >=3.1.2,<4.0a0 +- libjpeg-turbo >=3.1.4.1,<4.0a0 - libtiff >=4.7.1,<4.8.0a0 license: MIT license_family: MIT -size: 249959 -timestamp: 1768184673131 +size: 251086 +timestamp: 1778079286384 - conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.45.1-default_hbd61a6d_102.conda sha256: 3d584956604909ff5df353767f3a2a2f60e07d070b328d109f30ac40cd62df6c md5: 18335a698559cdbcd86150a48bf54ba6 @@ -509,37 +496,37 @@ license: Apache-2.0 license_family: Apache size: 261513 timestamp: 1773113328888 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libblas-3.11.0-5_h4a7cf45_openblas.conda -build_number: 5 -sha256: 18c72545080b86739352482ba14ba2c4815e19e26a7417ca21a95b76ec8da24c -md5: c160954f7418d7b6e87eaf05a8913fa9 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libblas-3.11.0-7_h4a7cf45_openblas.conda +build_number: 7 +sha256: 081c850f99bc355821fac9c6e3727d40b3f8ce3beb50a5437cf03726b611ff39 +md5: 955b44e8b00b7f7ef4ce0130cef12394 depends: -- libopenblas >=0.3.30,<0.3.31.0a0 -- libopenblas >=0.3.30,<1.0a0 +- libopenblas >=0.3.33,<0.3.34.0a0 +- libopenblas >=0.3.33,<1.0a0 constrains: -- mkl <2026 -- liblapack 3.11.0 5*_openblas -- libcblas 3.11.0 5*_openblas -- blas 2.305 openblas -- liblapacke 3.11.0 5*_openblas +- libcblas 3.11.0 7*_openblas +- blas 2.307 openblas +- liblapack 3.11.0 7*_openblas +- liblapacke 3.11.0 7*_openblas +- mkl <2027 license: BSD-3-Clause license_family: BSD -size: 18213 -timestamp: 1765818813880 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.11.0-5_h0358290_openblas.conda -build_number: 5 -sha256: 0cbdcc67901e02dc17f1d19e1f9170610bd828100dc207de4d5b6b8ad1ae7ad8 -md5: 6636a2b6f1a87572df2970d3ebc87cc0 -depends: -- libblas 3.11.0 5_h4a7cf45_openblas +size: 18716 +timestamp: 1778489854108 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.11.0-7_h0358290_openblas.conda +build_number: 7 +sha256: 956ae0bb1ec8b0c3663d75b151aceb0521b54e513bf97f621a035f9c87037970 +md5: 0675639dc24cb0032f199e7ff68e4633 +depends: +- libblas 3.11.0 7_h4a7cf45_openblas constrains: -- liblapacke 3.11.0 5*_openblas -- blas 2.305 openblas -- liblapack 3.11.0 5*_openblas +- liblapacke 3.11.0 7*_openblas +- blas 2.307 openblas +- liblapack 3.11.0 7*_openblas license: BSD-3-Clause license_family: BSD -size: 18194 -timestamp: 1765818837135 +size: 18675 +timestamp: 1778489861559 - conda: https://conda.anaconda.org/conda-forge/linux-64/libdeflate-1.25-h17f619e_0.conda sha256: aa8e8c4be9a2e81610ddf574e05b64ee131fab5e0e3693210c9d6d2fba32c680 md5: 6c77a605a7a689d17d4819c0f8ac9a00 @@ -550,18 +537,18 @@ license: MIT license_family: MIT size: 73490 timestamp: 1761979956660 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.7.4-hecca717_0.conda -sha256: d78f1d3bea8c031d2f032b760f36676d87929b18146351c4464c66b0869df3f5 -md5: e7f7ce06ec24cfcfb9e36d28cf82ba57 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.8.1-hecca717_0.conda +sha256: 363018b25fdb5534c79783d912bd4b685a3547f4fc5996357ad548899b0ee8e7 +md5: 93764a5ca80616e9c10106cdaec92f74 depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 constrains: -- expat 2.7.4.* +- expat 2.8.1.* license: MIT license_family: MIT -size: 76798 -timestamp: 1771259418166 +size: 77294 +timestamp: 1779278686680 - conda: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.5.2-h3435931_0.conda sha256: 31f19b6a88ce40ebc0d5a992c131f57d919f73c0b92cd1617a5bec83f6e961e6 md5: a360c33a5abe61c07959e449fa1453eb @@ -593,42 +580,42 @@ constrains: license: GPL-2.0-only OR FTL size: 384575 timestamp: 1774298162622 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-15.2.0-he0feb66_18.conda -sha256: faf7d2017b4d718951e3a59d081eb09759152f93038479b768e3d612688f83f5 -md5: 0aa00f03f9e39fb9876085dee11a85d4 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-15.2.0-he0feb66_19.conda +sha256: 8e0a3b5e41272e5678499b5dfc4cddb673f9e935de01eb0767ce857001229f46 +md5: 57736f29cc2b0ec0b6c2952d3f101b6a depends: - __glibc >=2.17,<3.0.a0 - _openmp_mutex >=4.5 constrains: -- libgcc-ng ==15.2.0=*_18 -- libgomp 15.2.0 he0feb66_18 +- libgcc-ng ==15.2.0=*_19 +- libgomp 15.2.0 he0feb66_19 license: GPL-3.0-only WITH GCC-exception-3.1 license_family: GPL -size: 1041788 -timestamp: 1771378212382 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-15.2.0-h69a702a_18.conda -sha256: e318a711400f536c81123e753d4c797a821021fb38970cebfb3f454126016893 -md5: d5e96b1ed75ca01906b3d2469b4ce493 +size: 1041084 +timestamp: 1778269013026 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-15.2.0-h69a702a_19.conda +sha256: 9dcf54adfaa5e861123c2da4f2f0451a685464ea7e5a41ad91cf67b31d658d98 +md5: 331ee9b72b9dff570d56b1302c5ab37d depends: -- libgcc 15.2.0 he0feb66_18 +- libgcc 15.2.0 he0feb66_19 license: GPL-3.0-only WITH GCC-exception-3.1 license_family: GPL -size: 27526 -timestamp: 1771378224552 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-15.2.0-h69a702a_18.conda -sha256: d2c9fad338fd85e4487424865da8e74006ab2e2475bd788f624d7a39b2a72aee -md5: 9063115da5bc35fdc3e1002e69b9ef6e +size: 27694 +timestamp: 1778269016987 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-15.2.0-h69a702a_19.conda +sha256: 561a42758ef25b9ce308c4e2cf56daee4f06138385a17e29a492cd928e00be6f +md5: 42bf7eca1a951735fa06c0e3c0d5c8e6 depends: -- libgfortran5 15.2.0 h68bc16d_18 +- libgfortran5 15.2.0 h68bc16d_19 constrains: -- libgfortran-ng ==15.2.0=*_18 +- libgfortran-ng ==15.2.0=*_19 license: GPL-3.0-only WITH GCC-exception-3.1 license_family: GPL -size: 27523 -timestamp: 1771378269450 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-15.2.0-h68bc16d_18.conda -sha256: 539b57cf50ec85509a94ba9949b7e30717839e4d694bc94f30d41c9d34de2d12 -md5: 646855f357199a12f02a87382d429b75 +size: 27655 +timestamp: 1778269042954 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-15.2.0-h68bc16d_19.conda +sha256: 057978bb69fea29ed715a9b98adf71015c31baecc4aeb2bfc20d4fd5d83579d4 +md5: 85072b0ad177c966294f129b7c04a2d5 depends: - __glibc >=2.17,<3.0.a0 - libgcc >=15.2.0 @@ -636,53 +623,53 @@ constrains: - libgfortran 15.2.0 license: GPL-3.0-only WITH GCC-exception-3.1 license_family: GPL -size: 2482475 -timestamp: 1771378241063 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-15.2.0-he0feb66_18.conda -sha256: 21337ab58e5e0649d869ab168d4e609b033509de22521de1bfed0c031bfc5110 -md5: 239c5e9546c38a1e884d69effcf4c882 +size: 2483673 +timestamp: 1778269025089 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-15.2.0-he0feb66_19.conda +sha256: 5abe4ab9d93f6c9757d654f1969ae2267d4505315c1f2f8fe705fd60af084f1b +md5: faac990cb7aedc7f3a2224f2c9b0c26c depends: - __glibc >=2.17,<3.0.a0 license: GPL-3.0-only WITH GCC-exception-3.1 license_family: GPL -size: 603262 -timestamp: 1771378117851 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libjpeg-turbo-3.1.2-hb03c661_0.conda -sha256: cc9aba923eea0af8e30e0f94f2ad7156e2984d80d1e8e7fe6be5a1f257f0eb32 -md5: 8397539e3a0bbd1695584fb4f927485a +size: 603817 +timestamp: 1778268942614 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libjpeg-turbo-3.1.4.1-hb03c661_0.conda +sha256: 10056646c28115b174de81a44e23e3a0a3b95b5347d2e6c45cc6d49d35294256 +md5: 6178c6f2fb254558238ef4e6c56fb782 depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 constrains: - jpeg <0.0.0a license: IJG AND BSD-3-Clause AND Zlib -size: 633710 -timestamp: 1762094827865 -- conda: https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.11.0-5_h47877c9_openblas.conda -build_number: 5 -sha256: c723b6599fcd4c6c75dee728359ef418307280fa3e2ee376e14e85e5bbdda053 -md5: b38076eb5c8e40d0106beda6f95d7609 -depends: -- libblas 3.11.0 5_h4a7cf45_openblas +size: 633831 +timestamp: 1775962768273 +- conda: https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.11.0-7_h47877c9_openblas.conda +build_number: 7 +sha256: 96962084921f197c9ad13fb7f8b324f2351d50ff3d8d962148751ad532f54a01 +md5: 6569b4f273740e25dc0dc7e3232c2a6c +depends: +- libblas 3.11.0 7_h4a7cf45_openblas constrains: -- blas 2.305 openblas -- liblapacke 3.11.0 5*_openblas -- libcblas 3.11.0 5*_openblas +- liblapacke 3.11.0 7*_openblas +- libcblas 3.11.0 7*_openblas +- blas 2.307 openblas license: BSD-3-Clause license_family: BSD -size: 18200 -timestamp: 1765818857876 -- conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.2-hb03c661_0.conda -sha256: 755c55ebab181d678c12e49cced893598f2bab22d582fbbf4d8b83c18be207eb -md5: c7c83eecbb72d88b940c249af56c8b17 +size: 18694 +timestamp: 1778489869038 +- conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.3-hb03c661_0.conda +sha256: ec30e52a3c1bf7d0425380a189d209a52baa03f22fb66dd3eb587acaa765bd6d +md5: b88d90cad08e6bc8ad540cb310a761fb depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 constrains: -- xz 5.8.2.* +- xz 5.8.3.* license: 0BSD -size: 113207 -timestamp: 1768752626120 +size: 113478 +timestamp: 1775825492909 - conda: https://conda.anaconda.org/conda-forge/linux-64/libmpdec-4.0.0-hb03c661_1.conda sha256: fe171ed5cf5959993d43ff72de7596e8ac2853e9021dec0344e583734f1e0843 md5: 2c21e66f50753a083cbe6b80f38268fa @@ -693,53 +680,52 @@ license: BSD-2-Clause license_family: BSD size: 92400 timestamp: 1769482286018 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.30-pthreads_h94d23a6_4.conda -sha256: 199d79c237afb0d4780ccd2fbf829cea80743df60df4705202558675e07dd2c5 -md5: be43915efc66345cccb3c310b6ed0374 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.33-pthreads_h94d23a6_0.conda +sha256: 3d9aa85648e5e18a6d66db98b8c4317cc426721ad7a220aa86330d1ccedc8903 +md5: 2d3278b721e40468295ca755c3b84070 depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 - libgfortran - libgfortran5 >=14.3.0 constrains: -- openblas >=0.3.30,<0.3.31.0a0 +- openblas >=0.3.33,<0.3.34.0a0 license: BSD-3-Clause license_family: BSD -size: 5927939 -timestamp: 1763114673331 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libpng-1.6.55-h421ea60_0.conda -sha256: 36ade759122cdf0f16e2a2562a19746d96cf9c863ffaa812f2f5071ebbe9c03c -md5: 5f13ffc7d30ffec87864e678df9957b4 +size: 5931919 +timestamp: 1776993658641 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libpng-1.6.58-h421ea60_0.conda +sha256: 377cfe037f3eeb3b1bf3ad333f724a64d32f315ee1958581fc671891d63d3f89 +md5: eba48a68a1a2b9d3c0d9511548db85db depends: -- libgcc >=14 - __glibc >=2.17,<3.0.a0 -- libzlib >=1.3.1,<2.0a0 +- libgcc >=14 +- libzlib >=1.3.2,<2.0a0 license: zlib-acknowledgement -size: 317669 -timestamp: 1770691470744 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.52.0-hf4e2dac_0.conda -sha256: d716847b7deca293d2e49ed1c8ab9e4b9e04b9d780aea49a97c26925b28a7993 -md5: fd893f6a3002a635b5e50ceb9dd2c0f4 +size: 317729 +timestamp: 1776315175087 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.53.1-h0c1763c_0.conda +sha256: 54cdcd3214313b62c2a8ee277e6f42150d9b748264c1b70d958bf735e420ef8d +md5: 7dc38adcbf71e6b38748e919e16e0dce depends: - __glibc >=2.17,<3.0.a0 -- icu >=78.2,<79.0a0 - libgcc >=14 -- libzlib >=1.3.1,<2.0a0 +- libzlib >=1.3.2,<2.0a0 license: blessing -size: 951405 -timestamp: 1772818874251 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-15.2.0-h934c35e_18.conda -sha256: 78668020064fdaa27e9ab65cd2997e2c837b564ab26ce3bf0e58a2ce1a525c6e -md5: 1b08cd684f34175e4514474793d44bcb +size: 954962 +timestamp: 1777986471789 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-15.2.0-h934c35e_19.conda +sha256: dff1058c76ec6b8759e41cefa2508162d00e4a5e6721aa68ec3fd10094e702dc +md5: 5794b3bdc38177caf969dabd3af08549 depends: - __glibc >=2.17,<3.0.a0 -- libgcc 15.2.0 he0feb66_18 +- libgcc 15.2.0 he0feb66_19 constrains: -- libstdcxx-ng ==15.2.0=*_18 +- libstdcxx-ng ==15.2.0=*_19 license: GPL-3.0-only WITH GCC-exception-3.1 license_family: GPL -size: 5852330 -timestamp: 1771378262446 +size: 5852044 +timestamp: 1778269036376 - conda: https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.7.1-h9d88235_1.conda sha256: e5f8c38625aa6d567809733ae04bb71c161a42e44a9fa8227abe61fa5c60ebe0 md5: cd5a90476766d53e901500df9215e927 @@ -757,16 +743,16 @@ depends: license: HPND size: 435273 timestamp: 1762022005702 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.41.3-h5347b49_0.conda -sha256: 1a7539cfa7df00714e8943e18de0b06cceef6778e420a5ee3a2a145773758aee -md5: db409b7c1720428638e7c0d509d3e1b5 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.42.1-h5347b49_0.conda +sha256: 3f0edf1280e2f6684a986f821eaa3e123d2694a00b31b96ca0d4a4c12c129231 +md5: 7d0a66598195ef00b6efc55aefc7453b depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 license: BSD-3-Clause license_family: BSD -size: 40311 -timestamp: 1766271528534 +size: 40163 +timestamp: 1779118517630 - conda: https://conda.anaconda.org/conda-forge/linux-64/libwebp-base-1.6.0-hd42ef1d_0.conda sha256: 3aed21ab28eddffdaf7f804f49be7a7d701e8f0e46c856d801270b470820a37b md5: aea31d2e5b1091feca96fcfe945c3cf9 @@ -814,16 +800,16 @@ license: BSD-3-Clause license_family: BSD size: 85893 timestamp: 1770694658918 -- conda: https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-4.0.0-pyhd8ed1ab_0.conda -sha256: 7b1da4b5c40385791dbc3cc85ceea9fad5da680a27d5d3cb8bfaa185e304a89e -md5: 5b5203189eb668f042ac2b0826244964 +- conda: https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-4.2.0-pyhd8ed1ab_0.conda +sha256: 0c4c35376fe920714390d46e4b8d31c876d65f18e1655899e0763ec25f2a902f +md5: 6d03368f2b2b0a5fb6839df53b2eb5e0 depends: - mdurl >=0.1,<1 - python >=3.10 license: MIT license_family: MIT -size: 64736 -timestamp: 1754951288511 +size: 69017 +timestamp: 1778169663339 - conda: https://conda.anaconda.org/conda-forge/linux-64/markupsafe-3.0.3-py314h67df5f8_1.conda sha256: c279be85b59a62d5c52f5dd9a4cd43ebd08933809a8416c22c3131595607d4cf md5: 9a17c4307d23318476d7fbf0fedc0cde @@ -854,9 +840,9 @@ license: MIT license_family: MIT size: 14465 timestamp: 1733255681319 -- conda: https://conda.anaconda.org/bioconda/noarch/multiqc-1.33-pyhdfd78af_0.conda -sha256: f005760b13093362fc9c997d603dd487de32ab2e821a3cbce52a42bcb8136517 -md5: 698a8a27c2b9d8a542c70cb47099a75e +- conda: https://conda.anaconda.org/bioconda/noarch/multiqc-1.35-pyhdfd78af_1.conda +sha256: e86033aa55a9e915e2d0957e770bdb81e3feb26a227d1adb17f9d6c528da6a71 +md5: cdb20309681ba3ce8f52c110e214d4f3 depends: - click - coloredlogs @@ -870,10 +856,11 @@ depends: - packaging - pillow >=10.2.0 - plotly >=5.18 -- polars-lts-cpu +- polars >=1.34.0 +- polars-runtime-compat >=1.34.0 - pyaml-env - pydantic >=2.7.1 -- python >=3.8,!=3.14.1 +- python >=3.9,!=3.14.1 - python-dotenv - python-kaleido 0.2.1 - pyyaml >=4 @@ -883,20 +870,21 @@ depends: - spectra >=0.0.10 - tiktoken - tqdm -- typeguard +- typeguard >=4 license: GPL-3.0-or-later license_family: GPL3 -size: 4198799 -timestamp: 1765300743879 -- conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-2.18.1-pyhcf101f3_1.conda -sha256: 541fd4390a0687228b8578247f1536a821d9261389a65585af9d1a6f2a14e1e0 -md5: 30bec5e8f4c3969e2b1bd407c5e52afb +size: 4282188 +timestamp: 1779465338806 +- conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-2.21.2-pyhcf101f3_0.conda +sha256: 70f43d62450927d51673eecd8823e14f5b3cfebdb43cda1d502eba97162bab42 +md5: 6687827c332121727ce383919e1ec8c2 depends: - python >=3.10 - python license: MIT -size: 280459 -timestamp: 1774380620329 +license_family: MIT +size: 284323 +timestamp: 1778929680962 - conda: https://conda.anaconda.org/conda-forge/noarch/natsort-8.4.0-pyhcf101f3_2.conda sha256: aeb1548eb72e4f198e72f19d242fb695b35add2ac7b2c00e0d83687052867680 md5: e941e85e273121222580723010bd4fa2 @@ -907,15 +895,15 @@ license: MIT license_family: MIT size: 39262 timestamp: 1770905275632 -- conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.5-h2d0b736_3.conda -sha256: 3fde293232fa3fca98635e1167de6b7c7fda83caf24b9d6c91ec9eefb4f4d586 -md5: 47e340acb35de30501a76c7c799c41d7 +- conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.6-hdb14827_0.conda +sha256: fc89f74bbe362fb29fa3c037697a89bec140b346a2469a90f7936d1d7ea4d8a3 +md5: fc21868a1a5aacc937e7a18747acb8a5 depends: - __glibc >=2.17,<3.0.a0 -- libgcc >=13 +- libgcc >=14 license: X11 AND BSD-3-Clause -size: 891641 -timestamp: 1738195959188 +size: 918956 +timestamp: 1777422145199 - conda: https://conda.anaconda.org/conda-forge/noarch/networkx-3.6.1-pyhcf101f3_0.conda sha256: f6a82172afc50e54741f6f84527ef10424326611503c64e359e25a19a8e4c1c6 md5: a2c1eeadae7a309daed9d62c96012a2b @@ -956,24 +944,24 @@ license: MPL-2.0 license_family: MOZILLA size: 2057773 timestamp: 1763485556350 -- conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.4.3-py314h2b28147_0.conda -sha256: f2ba8cb0d86a6461a6bcf0d315c80c7076083f72c6733c9290086640723f79ec -md5: 36f5b7eb328bdc204954a2225cf908e2 +- conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.4.6-py314h2b28147_0.conda +sha256: bc61ae892973751a6b0e6ecea57ed6d7053224bddcb007165d6ceb1d7344ad47 +md5: f49b5f950379e0b97c35ca97682f7c6a depends: - python - libstdcxx >=14 - libgcc >=14 - __glibc >=2.17,<3.0.a0 -- python_abi 3.14.* *_cp314 -- libcblas >=3.9.0,<4.0a0 - liblapack >=3.9.0,<4.0a0 +- python_abi 3.14.* *_cp314 - libblas >=3.9.0,<4.0a0 +- libcblas >=3.9.0,<4.0a0 constrains: - numpy-base <0a0 license: BSD-3-Clause license_family: BSD -size: 8927860 -timestamp: 1773839233468 +size: 8928909 +timestamp: 1779169198391 - conda: https://conda.anaconda.org/conda-forge/linux-64/openjpeg-2.5.4-h55fea9a_0.conda sha256: 3900f9f2dbbf4129cf3ad6acf4e4b6f7101390b53843591c53b00f034343bc4d md5: 11b3379b191f63139e29c0d19dee24cd @@ -988,48 +976,48 @@ license: BSD-2-Clause license_family: BSD size: 355400 timestamp: 1758489294972 -- conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.6.1-h35e630c_1.conda -sha256: 44c877f8af015332a5d12f5ff0fb20ca32f896526a7d0cdb30c769df1144fb5c -md5: f61eb8cd60ff9057122a3d338b99c00f +- conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.6.2-h35e630c_0.conda +sha256: c0ef482280e38c71a08ad6d71448194b719630345b0c9c60744a2010e8a8e0cb +md5: da1b85b6a87e141f5140bb9924cecab0 depends: - __glibc >=2.17,<3.0.a0 - ca-certificates - libgcc >=14 license: Apache-2.0 license_family: Apache -size: 3164551 -timestamp: 1769555830639 -- conda: https://conda.anaconda.org/conda-forge/noarch/packaging-26.0-pyhcf101f3_0.conda -sha256: c1fc0f953048f743385d31c468b4a678b3ad20caffdeaa94bed85ba63049fd58 -md5: b76541e68fea4d511b1ac46a28dcd2c6 +size: 3167099 +timestamp: 1775587756857 +- conda: https://conda.anaconda.org/conda-forge/noarch/packaging-26.2-pyhc364b38_0.conda +sha256: 3906abfb6511a3bb309e39b9b1b7bc38f50a723971de2395489fd1f379255890 +md5: 4c06a92e74452cfa53623a81592e8934 depends: - python >=3.8 - python license: Apache-2.0 license_family: APACHE -size: 72010 -timestamp: 1769093650580 -- conda: https://conda.anaconda.org/conda-forge/linux-64/pillow-12.1.1-py314h8ec4b1a_0.conda -sha256: 9e6ec8f3213e8b7d64b0ad45f84c51a2c9eba4398efda31e196c9a56186133ee -md5: 79678378ae235e24b3aa83cee1b38207 +size: 91574 +timestamp: 1777103621679 +- conda: https://conda.anaconda.org/conda-forge/linux-64/pillow-12.2.0-py314h8ec4b1a_0.conda +sha256: 123d8a7c16c88658b4f29e9f115a047598c941708dade74fbaff373a32dbec5e +md5: 76c4757c0ec9d11f969e8eb44899307b depends: - python - libgcc >=14 - __glibc >=2.17,<3.0.a0 +- libtiff >=4.7.1,<4.8.0a0 +- openjpeg >=2.5.4,<3.0a0 +- libxcb >=1.17.0,<2.0a0 - libwebp-base >=1.6.0,<2.0a0 - zlib-ng >=2.3.3,<2.4.0a0 -- python_abi 3.14.* *_cp314 -- tk >=8.6.13,<8.7.0a0 - libjpeg-turbo >=3.1.2,<4.0a0 -- libxcb >=1.17.0,<2.0a0 -- openjpeg >=2.5.4,<3.0a0 +- python_abi 3.14.* *_cp314 +- libfreetype >=2.14.3 +- libfreetype6 >=2.14.3 - lcms2 >=2.18,<3.0a0 -- libtiff >=4.7.1,<4.8.0a0 -- libfreetype >=2.14.1 -- libfreetype6 >=2.14.1 +- tk >=8.6.13,<8.7.0a0 license: HPND -size: 1073026 -timestamp: 1770794002408 +size: 1082797 +timestamp: 1775060059882 - conda: https://conda.anaconda.org/conda-forge/noarch/plotly-6.6.0-pyhd8ed1ab_0.conda sha256: c418d325359fc7a0074cea7f081ef1bce26e114d2da8a0154c5d27ecc87a08e7 md5: 3e9427ee186846052e81fadde8ebe96a @@ -1043,11 +1031,11 @@ license: MIT license_family: MIT size: 5251872 timestamp: 1772628857717 -- conda: https://conda.anaconda.org/conda-forge/noarch/polars-1.39.3-pyh58ad624_1.conda -sha256: d332c2d5002fc440ae37ed9679ffc21b552f18d20232390005d1dd3bce0888d3 -md5: d5a4e013a30dd8dfde9ab39f45aaf9c1 +- conda: https://conda.anaconda.org/conda-forge/noarch/polars-1.41.0-pyh58ad624_0.conda +sha256: 70fc56877c4a095ee658d61924d8019768fbae4a48437058d181fc94b0a7c4d8 +md5: 25a883fed9f1f3f21ff317a3e7c92ac4 depends: -- polars-runtime-32 ==1.39.3 +- polars-runtime-32 ==1.41.0 - python >=3.10 - python constrains: @@ -1061,57 +1049,44 @@ constrains: - pyiceberg >=0.7.1 - altair >=5.4.0 - great_tables >=0.8.0 -- polars-runtime-32 ==1.39.3 -- polars-runtime-64 ==1.39.3 -- polars-runtime-compat ==1.39.3 +- polars-runtime-32 ==1.41.0 +- polars-runtime-64 ==1.41.0 +- polars-runtime-compat ==1.41.0 license: MIT -license_family: MIT -size: 533495 -timestamp: 1774207987966 -- conda: https://conda.anaconda.org/conda-forge/noarch/polars-lts-cpu-1.34.0.deprecated-hc364b38_0.conda -sha256: e466fb31f67ba9bde18deafeb34263ca5eb25807f39ead0e9d753a8e82c4c4f4 -md5: ef0340e75068ac8ff96462749b5c98e7 -depends: -- polars >=1.34.0 -- polars-runtime-compat >=1.34.0 -license: MIT -license_family: MIT -size: 3902 -timestamp: 1760206808444 -- conda: https://conda.anaconda.org/conda-forge/linux-64/polars-runtime-32-1.39.3-py310hffdcd12_1.conda +size: 539656 +timestamp: 1779630790562 +- conda: https://conda.anaconda.org/conda-forge/linux-64/polars-runtime-32-1.41.0-py310h49dadd8_0.conda noarch: python -sha256: 9744f8086bb0832998f5b01076f57ddc9efbe460e493b14303c3567dc4f401e7 -md5: f9327f9f2cfc4215f55b613e64afd3ba +sha256: e51ee3fe5259f2e115b2f78f8fbe3554e419c7c82b0c110878e12a5ff95ce3ab +md5: 7682765a1588e5ac887c99736d297c93 depends: - python +- __glibc >=2.17,<3.0.a0 - libstdcxx >=14 - libgcc >=14 -- __glibc >=2.17,<3.0.a0 - _python_abi3_support 1.* - cpython >=3.10 constrains: - __glibc >=2.17 license: MIT -license_family: MIT -size: 37570276 -timestamp: 1774207987966 -- conda: https://conda.anaconda.org/conda-forge/linux-64/polars-runtime-compat-1.39.3-py310hbcd5346_1.conda +size: 42578921 +timestamp: 1779630790562 +- conda: https://conda.anaconda.org/conda-forge/linux-64/polars-runtime-compat-1.41.0-py310hcbd6021_0.conda noarch: python -sha256: bf0b932713f0f27924f42159c98426e0073bb6145ed796eaa4cec79ca05363c7 -md5: 4b9b312453eebd6fbdbbe2a88fa1b5c4 +sha256: 29c3831c92394af11d9f7d04882dda9479ffbb76a3d36ba155d52159d67805fa +md5: cb0b620c9914a07a9022cb8b183ea9ee depends: - python -- libgcc >=14 - libstdcxx >=14 +- libgcc >=14 - __glibc >=2.17,<3.0.a0 - _python_abi3_support 1.* - cpython >=3.10 constrains: - __glibc >=2.17 license: MIT -license_family: MIT -size: 37224264 -timestamp: 1774207985377 +size: 41864944 +timestamp: 1779630722548 - conda: https://conda.anaconda.org/conda-forge/linux-64/procps-ng-4.0.6-h18c060e_0.conda sha256: 4ce2e1ee31a6217998f78c31ce7dc0a3e0557d9238b51d49dd20c52d467a126d md5: f2c23a77b25efcad57d377b34bd84941 @@ -1143,24 +1118,23 @@ license: MIT license_family: MIT size: 14645 timestamp: 1736766960536 -- conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-2.12.5-pyhcf101f3_1.conda -sha256: 868569d9505b7fe246c880c11e2c44924d7613a8cdcc1f6ef85d5375e892f13d -md5: c3946ed24acdb28db1b5d63321dbca7d +- conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-2.13.4-pyhcf101f3_0.conda +sha256: 69700e31165df070e9716315e042196aa92525dae5deb5107785847ab9f4189f +md5: 729843edafc0899b3348bd3f19525b9d depends: - typing-inspection >=0.4.2 - typing_extensions >=4.14.1 - python >=3.10 -- typing-extensions >=4.6.1 - annotated-types >=0.6.0 -- pydantic-core ==2.41.5 +- pydantic-core ==2.46.4 - python license: MIT license_family: MIT -size: 340482 -timestamp: 1764434463101 -- conda: https://conda.anaconda.org/conda-forge/linux-64/pydantic-core-2.41.5-py314h2e6c369_1.conda -sha256: 7e0ae379796e28a429f8e48f2fe22a0f232979d65ec455e91f8dac689247d39f -md5: 432b0716a1dfac69b86aa38fdd59b7e6 +size: 346511 +timestamp: 1778103405862 +- conda: https://conda.anaconda.org/conda-forge/linux-64/pydantic-core-2.46.4-py314h2e6c369_0.conda +sha256: 802e216c39f1359aed60823b6e11d8ccd812b0ae1c81ae5ac1c81f99446409ab +md5: 0c96993dbeadf3a277cf757b9f1c9412 depends: - python - typing-extensions >=4.6.0,!=4.7.0 @@ -1171,17 +1145,17 @@ constrains: - __glibc >=2.17 license: MIT license_family: MIT -size: 1943088 -timestamp: 1762988995556 -- conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.19.2-pyhd8ed1ab_0.conda -sha256: 5577623b9f6685ece2697c6eb7511b4c9ac5fb607c9babc2646c811b428fd46a -md5: 6b6ece66ebcae2d5f326c77ef2c5a066 +size: 1895020 +timestamp: 1778084229247 +- conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.20.0-pyhd8ed1ab_0.conda +sha256: cf70b2f5ad9ae472b71235e5c8a736c9316df3705746de419b59d442e8348e86 +md5: 16c18772b340887160c79a6acc022db0 depends: -- python >=3.9 +- python >=3.10 license: BSD-2-Clause license_family: BSD -size: 889287 -timestamp: 1750615908735 +size: 893031 +timestamp: 1774796815820 - conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda sha256: ba3b032fa52709ce0d9fd388f63d330a026754587a2f461117cac9ab73d8d0d8 md5: 461219d1a5bd61342293efa2c0c90eac @@ -1192,32 +1166,32 @@ license: BSD-3-Clause license_family: BSD size: 21085 timestamp: 1733217331982 -- conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.14.3-h32b2ec7_101_cp314.conda -build_number: 101 -sha256: cb0628c5f1732f889f53a877484da98f5a0e0f47326622671396fb4f2b0cd6bd -md5: c014ad06e60441661737121d3eae8a60 +- conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.14.5-habeac84_100_cp314.conda +build_number: 100 +sha256: 55eed9bf2a3f6e90311276f0834737fe7c2d9ec3e5e2e557507858df4c7521e6 +md5: da92e59ff92f2d5ede4f612af20f583f depends: - __glibc >=2.17,<3.0.a0 - bzip2 >=1.0.8,<2.0a0 - ld_impl_linux-64 >=2.36.1 -- libexpat >=2.7.3,<3.0a0 +- libexpat >=2.8.0,<3.0a0 - libffi >=3.5.2,<3.6.0a0 - libgcc >=14 -- liblzma >=5.8.2,<6.0a0 +- liblzma >=5.8.3,<6.0a0 - libmpdec >=4.0.0,<5.0a0 -- libsqlite >=3.51.2,<4.0a0 -- libuuid >=2.41.3,<3.0a0 -- libzlib >=1.3.1,<2.0a0 -- ncurses >=6.5,<7.0a0 -- openssl >=3.5.5,<4.0a0 +- libsqlite >=3.53.1,<4.0a0 +- libuuid >=2.42.1,<3.0a0 +- libzlib >=1.3.2,<2.0a0 +- ncurses >=6.6,<7.0a0 +- openssl >=3.5.6,<4.0a0 - python_abi 3.14.* *_cp314 - readline >=8.3,<9.0a0 - tk >=8.6.13,<8.7.0a0 - tzdata - zstd >=1.5.7,<1.6.0a0 license: Python-2.0 -size: 36702440 -timestamp: 1770675584356 +size: 36745188 +timestamp: 1779236923603 python_site_packages_path: lib/python3.14/site-packages - conda: https://conda.anaconda.org/conda-forge/noarch/python-dotenv-1.2.2-pyhcf101f3_0.conda sha256: 74e417a768f59f02a242c25e7db0aa796627b5bc8c818863b57786072aeb85e5 @@ -1228,15 +1202,15 @@ license: BSD-3-Clause license_family: BSD size: 27848 timestamp: 1772388605021 -- conda: https://conda.anaconda.org/conda-forge/noarch/python-gil-3.14.3-h4df99d1_101.conda -sha256: 233aebd94c704ac112afefbb29cf4170b7bc606e22958906f2672081bc50638a -md5: 235765e4ea0d0301c75965985163b5a1 +- conda: https://conda.anaconda.org/conda-forge/noarch/python-gil-3.14.5-h4df99d1_100.conda +sha256: 41dd7da285d71d519257fa7dacb1cae060d5ebfaa5f92cba5994899d2978e943 +md5: 41954747ba952ec4b01e16c2c9e8d8ff depends: -- cpython 3.14.3.* +- cpython 3.14.5.* - python_abi * *_cp314 license: Python-2.0 -size: 50062 -timestamp: 1770674497152 +size: 50212 +timestamp: 1779236703009 - conda: https://conda.anaconda.org/conda-forge/noarch/python-kaleido-0.2.1-pyhd8ed1ab_0.tar.bz2 sha256: e17bf63a30aec33432f1ead86e15e9febde9fc40a7f869c0e766be8d2db44170 md5: 310259a5b03ff02289d7705f39e2b1d2 @@ -1294,9 +1268,9 @@ license: MIT license_family: MIT size: 51788 timestamp: 1760379115194 -- conda: https://conda.anaconda.org/conda-forge/linux-64/regex-2026.2.28-py314h5bd0f2a_0.conda -sha256: e085e336f1446f5263a3ec9747df8c719b6996753901181add50dc4fdd8bb2e8 -md5: 3c8b6a8c4d0ff5a264e9831eac4941f4 +- conda: https://conda.anaconda.org/conda-forge/linux-64/regex-2026.5.9-py314h5bd0f2a_0.conda +sha256: c7a4aca4977c15c82d053b06cbc676460974c1b25757cfeea8a9a2497ac911f8 +md5: 9dd235b6ac69a0198080dac39f9891aa depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 @@ -1304,27 +1278,27 @@ depends: - python_abi 3.14.* *_cp314 license: Apache-2.0 AND CNRI-Python license_family: PSF -size: 411924 -timestamp: 1772255161535 -- conda: https://conda.anaconda.org/conda-forge/noarch/requests-2.32.5-pyhcf101f3_1.conda -sha256: 7813c38b79ae549504b2c57b3f33394cea4f2ad083f0994d2045c2e24cb538c5 -md5: c65df89a0b2e321045a9e01d1337b182 +size: 413611 +timestamp: 1778374155646 +- conda: https://conda.anaconda.org/conda-forge/noarch/requests-2.34.2-pyhcf101f3_0.conda +sha256: 1715246b19c9f85ee022933b4845f2fc14ac9184981b7b7d9b728bec8e9588da +md5: 4a85203c1d80c1059086ae860836ffb9 depends: - python >=3.10 -- certifi >=2017.4.17 +- certifi >=2023.5.7 - charset-normalizer >=2,<4 - idna >=2.5,<4 -- urllib3 >=1.21.1,<3 +- urllib3 >=1.26,<3 - python constrains: -- chardet >=3.0.2,<6 +- chardet >=3.0.2,<8 license: Apache-2.0 license_family: APACHE -size: 63602 -timestamp: 1766926974520 -- conda: https://conda.anaconda.org/conda-forge/noarch/rich-14.3.3-pyhcf101f3_0.conda -sha256: b06ce84d6a10c266811a7d3adbfa1c11f13393b91cc6f8a5b468277d90be9590 -md5: 7a6289c50631d620652f5045a63eb573 +size: 68709 +timestamp: 1778851103479 +- conda: https://conda.anaconda.org/conda-forge/noarch/rich-15.0.0-pyhcf101f3_0.conda +sha256: 3d6ba2c0fcdac3196ba2f0615b4104e532525ffa1335b50a2878be5ff488814a +md5: 0242025a3c804966bf71aa04eee82f66 depends: - markdown-it-py >=2.2.0 - pygments >=2.13.0,<3.0.0 @@ -1333,8 +1307,8 @@ depends: - python license: MIT license_family: MIT -size: 208472 -timestamp: 1771572730357 +size: 208577 +timestamp: 1775991661559 - conda: https://conda.anaconda.org/conda-forge/noarch/rich-click-1.9.7-pyh8f84b5b_0.conda sha256: aa3fcb167321bae51998de2e94d199109c9024f25a5a063cb1c28d8f1af33436 md5: 0c20a8ebcddb24a45da89d5e917e6cb9 @@ -1373,20 +1347,19 @@ license: MIT license_family: MIT size: 22284 timestamp: 1735770589188 -- conda: https://conda.anaconda.org/conda-forge/linux-64/sqlite-3.52.0-h04a0ce9_0.conda -sha256: c9af81e7830d9c4b67a7f48e512d060df2676b29cac59e3b31f09dbfcee29c58 -md5: 7d9d7efe9541d4bb71b5934e8ee348ea +- conda: https://conda.anaconda.org/conda-forge/linux-64/sqlite-3.53.1-hbc0de68_0.conda +sha256: d167fa92781bcdcd3b9aaa6bb1cd50c5b108f6190c170098a118b5cf5df2f881 +md5: 8e0b8654ead18e50af552e54b5a08a61 depends: - __glibc >=2.17,<3.0.a0 -- icu >=78.2,<79.0a0 - libgcc >=14 -- libsqlite 3.52.0 hf4e2dac_0 -- libzlib >=1.3.1,<2.0a0 -- ncurses >=6.5,<7.0a0 +- libsqlite 3.53.1 h0c1763c_0 +- libzlib >=1.3.2,<2.0a0 +- ncurses >=6.6,<7.0a0 - readline >=8.3,<9.0a0 license: blessing -size: 203641 -timestamp: 1772818888368 +size: 205399 +timestamp: 1777986477546 - conda: https://conda.anaconda.org/conda-forge/linux-64/tiktoken-0.12.0-py314h67fec18_3.conda sha256: 7e395d67fd249d901beb1ae269057763c0d8c3ee5f7a348694bdb16d158a37d9 md5: d705f9d8a1185a2b01cced191177a028 @@ -1427,20 +1400,20 @@ depends: license: MPL-2.0 and MIT size: 94132 timestamp: 1770153424136 -- conda: https://conda.anaconda.org/conda-forge/noarch/typeguard-4.5.1-pyhd8ed1ab_0.conda -sha256: 39d8ae33c43cdb8f771373e149b0b4fae5a08960ac58dcca95b2f1642bb17448 -md5: 260af1b0a94f719de76b4e14094e9a3b +- conda: https://conda.anaconda.org/conda-forge/noarch/typeguard-4.5.2-pyhcf101f3_0.conda +sha256: 59d7851d32fddb5b510272e6557aa982edeb927d349648dac27f5bf01d18bb26 +md5: 4460f039b7dedf15f7df086446ca75ae depends: -- importlib-metadata >=3.6 -- python >=3.10 -- typing-extensions >=4.10.0 - typing_extensions >=4.14.0 +- python >=3.10 +- importlib-metadata >=3.6 +- python constrains: - pytest >=7 license: MIT license_family: MIT -size: 36838 -timestamp: 1771532971545 +size: 38297 +timestamp: 1778779291237 - conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.15.0-h396c80c_0.conda sha256: 7c2df5721c742c2a47b2c8f960e718c930031663ac1174da67c1ed5999f7938c md5: edd329d7d3a4ab45dcf905899a7a6115 @@ -1450,16 +1423,17 @@ license: PSF-2.0 license_family: PSF size: 91383 timestamp: 1756220668932 -- conda: https://conda.anaconda.org/conda-forge/noarch/typing-inspection-0.4.2-pyhd8ed1ab_1.conda -sha256: 70db27de58a97aeb7ba7448366c9853f91b21137492e0b4430251a1870aa8ff4 -md5: a0a4a3035667fc34f29bfbd5c190baa6 +- conda: https://conda.anaconda.org/conda-forge/noarch/typing-inspection-0.4.2-pyhcf101f3_2.conda +sha256: 8b90d2f19f9458b8c58a55e1fcdc1d90c1603a847a47654d8a454549413ba60a +md5: 53f5409c5cfd6c5a66417d68e3f0a864 depends: - python >=3.10 - typing_extensions >=4.12.0 +- python license: MIT license_family: MIT -size: 18923 -timestamp: 1764158430324 +size: 20935 +timestamp: 1777105465795 - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda sha256: 032271135bca55aeb156cee361c81350c6f3fb203f57d024d7e5a1fc9ef18731 md5: 0caa1af407ecff61170c9437a808404d @@ -1476,9 +1450,9 @@ md5: ad659d0a2b3e47e38d829aa8cad2d610 license: LicenseRef-Public-Domain size: 119135 timestamp: 1767016325805 -- conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.6.3-pyhd8ed1ab_0.conda -sha256: af641ca7ab0c64525a96fd9ad3081b0f5bcf5d1cbb091afb3f6ed5a9eee6111a -md5: 9272daa869e03efe68833e3dc7a02130 +- conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.7.0-pyhd8ed1ab_0.conda +sha256: feff959a816f7988a0893201aa9727bbb7ee1e9cec2c4f0428269b489eb93fb4 +md5: cbb88288f74dbe6ada1c6c7d0a97223e depends: - backports.zstd >=1.0.0 - brotli-python >=1.2.0 @@ -1487,8 +1461,8 @@ depends: - python >=3.10 license: MIT license_family: MIT -size: 103172 -timestamp: 1767817860341 +size: 103560 +timestamp: 1778188657149 - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxau-1.0.12-hb03c661_1.conda sha256: 6bc6ab7a90a5d8ac94c7e300cc10beb0500eeba4b99822768ca2f2ef356f731b md5: b2895afaf55bf96a8c8282a2e47a5de0 @@ -1519,16 +1493,16 @@ license: MIT license_family: MIT size: 85189 timestamp: 1753484064210 -- conda: https://conda.anaconda.org/conda-forge/noarch/zipp-3.23.0-pyhcf101f3_1.conda -sha256: b4533f7d9efc976511a73ef7d4a2473406d7f4c750884be8e8620b0ce70f4dae -md5: 30cd29cb87d819caead4d55184c1d115 +- conda: https://conda.anaconda.org/conda-forge/noarch/zipp-4.1.0-pyhcf101f3_0.conda +sha256: 210bd31c22bb88f5e2a167df24c95bb5f152b2ada7502f9b8c49d1f5366db423 +md5: ba3dcdc8584155c97c648ae9c044b7a3 depends: - python >=3.10 - python license: MIT license_family: MIT -size: 24194 -timestamp: 1764460141901 +size: 24190 +timestamp: 1779159948016 - conda: https://conda.anaconda.org/conda-forge/linux-64/zlib-ng-2.3.3-hceb46e0_1.conda sha256: ea4e50c465d70236408cb0bfe0115609fd14db1adcd8bd30d8918e0291f8a75f md5: 2aadb0d17215603a82a2a6b0afd9a4cb diff --git a/modules/nf-core/multiqc/.conda-lock/linux_amd64-bd-db7c73dae76bc9e6_1.txt b/modules/nf-core/multiqc/.conda-lock/linux_amd64-bd-db7c73dae76bc9e6_1.txt deleted file mode 100644 index a55a4d4..0000000 --- a/modules/nf-core/multiqc/.conda-lock/linux_amd64-bd-db7c73dae76bc9e6_1.txt +++ /dev/null @@ -1,126 +0,0 @@ - -# This file may be used to create an environment using: -# $ conda create --name --file -# platform: linux-64 -@EXPLICIT -https://conda.anaconda.org/conda-forge/linux-64/libgomp-15.2.0-he0feb66_18.conda#239c5e9546c38a1e884d69effcf4c882 -https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-20_gnu.conda#a9f577daf3de00bca7c3c76c0ecbd1de -https://conda.anaconda.org/conda-forge/linux-64/libgcc-15.2.0-he0feb66_18.conda#0aa00f03f9e39fb9876085dee11a85d4 -https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-hda65f42_9.conda#d2ffd7602c02f2b316fd921d39876885 -https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.2-h25fd6f3_2.conda#d87ff7921124eccd67248aa483c23fec -https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb78ec9c_6.conda#4a13eeac0b5c8e5b8ab496e6c4ddd829 -https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.45.1-default_hbd61a6d_102.conda#18335a698559cdbcd86150a48bf54ba6 -https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.7.5-hecca717_0.conda#49f570f3bc4c874a06ea69b7225753af -https://conda.anaconda.org/conda-forge/linux-64/libffi-3.5.2-h3435931_0.conda#a360c33a5abe61c07959e449fa1453eb -https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.3-hb03c661_0.conda#b88d90cad08e6bc8ad540cb310a761fb -https://conda.anaconda.org/conda-forge/linux-64/libmpdec-4.0.0-hb03c661_1.conda#2c21e66f50753a083cbe6b80f38268fa -https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-15.2.0-h934c35e_18.conda#1b08cd684f34175e4514474793d44bcb -https://conda.anaconda.org/conda-forge/linux-64/icu-78.3-h33c6efd_0.conda#c80d8a3b84358cb967fa81e7075fbc8a -https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.53.0-hf4e2dac_0.conda#810d83373448da85c3f673fbcb7ad3a3 -https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.42-h5347b49_0.conda#38ffe67b78c9d4de527be8315e5ada2c -https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.5-h2d0b736_3.conda#47e340acb35de30501a76c7c799c41d7 -https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.4.22-hbd8a1cb_0.conda#e18ad67cf881dcadee8b8d9e2f8e5f73 -https://conda.anaconda.org/conda-forge/linux-64/openssl-3.6.2-h35e630c_0.conda#da1b85b6a87e141f5140bb9924cecab0 -https://conda.anaconda.org/conda-forge/noarch/python_abi-3.14-8_cp314.conda#0539938c55b6b1a59b560e843ad864a4 -https://conda.anaconda.org/conda-forge/linux-64/readline-8.3-h853b02a_0.conda#d7d95fc8287ea7bf33e0e7116d2b95ec -https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_h366c992_103.conda#cffd3bdd58090148f4cfcd831f4b26ab -https://conda.anaconda.org/conda-forge/noarch/tzdata-2025c-hc9c84f9_1.conda#ad659d0a2b3e47e38d829aa8cad2d610 -https://conda.anaconda.org/conda-forge/linux-64/python-3.14.4-habeac84_100_cp314.conda#a443f87920815d41bfe611296e507995 -https://conda.anaconda.org/conda-forge/noarch/cpython-3.14.4-py314hd8ed1ab_100.conda#f111d4cfaf1fe9496f386bc98ae94452 -https://conda.anaconda.org/conda-forge/noarch/python-gil-3.14.4-h4df99d1_100.conda#e4e60721757979d01d3964122f674959 -https://conda.anaconda.org/conda-forge/noarch/_python_abi3_support-1.0-hd8ed1ab_2.conda#aaa2a381ccc56eac91d63b6c1240312f -https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda#0caa1af407ecff61170c9437a808404d -https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.15.0-h396c80c_0.conda#edd329d7d3a4ab45dcf905899a7a6115 -https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.7.0-pyhd8ed1ab_1.conda#2934f256a8acfe48f6ebb4fce6cde29c -https://conda.anaconda.org/conda-forge/noarch/attrs-26.1.0-pyhcf101f3_0.conda#c6b0543676ecb1fb2d7643941fe375f2 -https://conda.anaconda.org/conda-forge/noarch/backports.zstd-1.3.0-py314h680f03e_0.conda#a2ac7763a9ac75055b68f325d3255265 -https://conda.anaconda.org/conda-forge/linux-64/brotli-python-1.2.0-py314h3de4e8d_1.conda#8910d2c46f7e7b519129f486e0fe927a -https://conda.anaconda.org/conda-forge/noarch/certifi-2026.4.22-pyhd8ed1ab_0.conda#929471569c93acefb30282a22060dcd5 -https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.7-pyhd8ed1ab_0.conda#a9167b9571f3baa9d448faa2139d1089 -https://conda.anaconda.org/conda-forge/noarch/click-8.3.2-pyhc90fa1f_0.conda#4d18bc3af7cfcea97bd817164672a08c -https://conda.anaconda.org/conda-forge/noarch/humanfriendly-10.0-pyh707e725_8.conda#7fe569c10905402ed47024fc481bb371 -https://conda.anaconda.org/conda-forge/noarch/coloredlogs-15.0.1-pyhd8ed1ab_4.conda#b866ff7007b934d564961066c8195983 -https://conda.anaconda.org/conda-forge/noarch/networkx-3.6.1-pyhcf101f3_0.conda#a2c1eeadae7a309daed9d62c96012a2b -https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-15.2.0-h68bc16d_18.conda#646855f357199a12f02a87382d429b75 -https://conda.anaconda.org/conda-forge/linux-64/libgfortran-15.2.0-h69a702a_18.conda#9063115da5bc35fdc3e1002e69b9ef6e -https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.32-pthreads_h94d23a6_0.conda#89d61bc91d3f39fda0ca10fcd3c68594 -https://conda.anaconda.org/conda-forge/linux-64/libblas-3.11.0-6_h4a7cf45_openblas.conda#6d6d225559bfa6e2f3c90ee9c03d4e2e -https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.11.0-6_h0358290_openblas.conda#36ae340a916635b97ac8a0655ace2a35 -https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.11.0-6_h47877c9_openblas.conda#881d801569b201c2e753f03c84b85e15 -https://conda.anaconda.org/conda-forge/linux-64/numpy-2.4.3-py314h2b28147_0.conda#36f5b7eb328bdc204954a2225cf908e2 -https://conda.anaconda.org/conda-forge/noarch/colormath-3.0.0-pyhd8ed1ab_4.conda#071cf7b0ce333c81718b054066c15102 -https://conda.anaconda.org/conda-forge/linux-64/expat-2.7.5-hecca717_0.conda#7de50d165039df32d38be74c1b34a910 -https://conda.anaconda.org/conda-forge/noarch/font-ttf-dejavu-sans-mono-2.37-hab24e00_0.tar.bz2#0c96522c6bdaed4b1566d11387caaf45 -https://conda.anaconda.org/conda-forge/noarch/font-ttf-inconsolata-3.000-h77eed37_0.tar.bz2#34893075a5c9e55cdafac56607368fc6 -https://conda.anaconda.org/conda-forge/noarch/font-ttf-source-code-pro-2.038-h77eed37_0.tar.bz2#4d59c254e01d9cde7957100457e2d5fb -https://conda.anaconda.org/conda-forge/noarch/font-ttf-ubuntu-0.83-h77eed37_3.conda#49023d73832ef61042f6a237cb2687e7 -https://conda.anaconda.org/conda-forge/linux-64/libpng-1.6.58-h421ea60_0.conda#eba48a68a1a2b9d3c0d9511548db85db -https://conda.anaconda.org/conda-forge/linux-64/libfreetype6-2.14.3-h73754d4_0.conda#fb16b4b69e3f1dcfe79d80db8fd0c55d -https://conda.anaconda.org/conda-forge/linux-64/libfreetype-2.14.3-ha770c72_0.conda#e289f3d17880e44b633ba911d57a321b -https://conda.anaconda.org/conda-forge/linux-64/fontconfig-2.17.1-h27c8c51_0.conda#867127763fbe935bab59815b6e0b7b5c -https://conda.anaconda.org/conda-forge/noarch/fonts-conda-forge-1-hc364b38_1.conda#a7970cd949a077b7cb9696379d338681 -https://conda.anaconda.org/conda-forge/noarch/hpack-4.1.0-pyhd8ed1ab_0.conda#0a802cb9888dd14eeefc611f05c40b6e -https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.1.0-pyhd8ed1ab_0.conda#8e6923fc12f1fe8f8c4e5c9f343256ac -https://conda.anaconda.org/conda-forge/noarch/h2-4.3.0-pyhcf101f3_0.conda#164fc43f0b53b6e3a7bc7dce5e4f1dc9 -https://conda.anaconda.org/conda-forge/noarch/humanize-4.15.0-pyhd8ed1ab_0.conda#daddf757c3ecd6067b9af1df1f25d89e -https://conda.anaconda.org/conda-forge/noarch/idna-3.13-pyhcf101f3_0.conda#fb7130c190f9b4ec91219840a05ba3ac -https://conda.anaconda.org/conda-forge/noarch/zipp-3.23.1-pyhcf101f3_0.conda#e1c36c6121a7c9c76f2f148f1e83b983 -https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.8.0-pyhcf101f3_0.conda#080594bf4493e6bae2607e65390c520a -https://conda.anaconda.org/conda-forge/linux-64/markupsafe-3.0.3-py314h67df5f8_1.conda#9a17c4307d23318476d7fbf0fedc0cde -https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.6-pyhcf101f3_1.conda#04558c96691bed63104678757beb4f8d -https://conda.anaconda.org/conda-forge/linux-64/rpds-py-0.30.0-py314h2e6c369_0.conda#c1c368b5437b0d1a68f372ccf01cb133 -https://conda.anaconda.org/conda-forge/noarch/referencing-0.37.0-pyhcf101f3_0.conda#870293df500ca7e18bedefa5838a22ab -https://conda.anaconda.org/conda-forge/noarch/jsonschema-specifications-2025.9.1-pyhcf101f3_0.conda#439cd0f567d697b20a8f45cb70a1005a -https://conda.anaconda.org/conda-forge/noarch/jsonschema-4.26.0-pyhcf101f3_0.conda#ada41c863af263cc4c5fcbaff7c3e4dc -https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-15.2.0-h69a702a_18.conda#d5e96b1ed75ca01906b3d2469b4ce493 -https://conda.anaconda.org/conda-forge/linux-64/mathjax-2.7.7-ha770c72_3.tar.bz2#86e69bd82c2a2c6fd29f5ab7e02b3691 -https://conda.anaconda.org/conda-forge/linux-64/nspr-4.38-h29cc59b_0.conda#e235d5566c9cc8970eb2798dd4ecf62f -https://conda.anaconda.org/conda-forge/linux-64/nss-3.118-h445c969_0.conda#567fbeed956c200c1db5782a424e58ee -https://conda.anaconda.org/conda-forge/linux-64/sqlite-3.53.0-h04a0ce9_0.conda#dc540e5bd5616d83a1ec46af8315ff98 -https://conda.anaconda.org/conda-forge/linux-64/kaleido-core-0.2.1-h3644ca4_0.tar.bz2#b3723b235b0758abaae8c82ce4d80146 -https://conda.anaconda.org/conda-forge/linux-64/libjpeg-turbo-3.1.4.1-hb03c661_0.conda#6178c6f2fb254558238ef4e6c56fb782 -https://conda.anaconda.org/conda-forge/linux-64/lerc-4.1.0-hdb68285_0.conda#a752488c68f2e7c456bcbd8f16eec275 -https://conda.anaconda.org/conda-forge/linux-64/libdeflate-1.25-h17f619e_0.conda#6c77a605a7a689d17d4819c0f8ac9a00 -https://conda.anaconda.org/conda-forge/linux-64/libwebp-base-1.6.0-hd42ef1d_0.conda#aea31d2e5b1091feca96fcfe945c3cf9 -https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.7.1-h9d88235_1.conda#cd5a90476766d53e901500df9215e927 -https://conda.anaconda.org/conda-forge/linux-64/lcms2-2.18-h0c24ade_0.conda#6f2e2c8f58160147c4d1c6f4c14cbac4 -https://conda.anaconda.org/conda-forge/linux-64/pthread-stubs-0.4-hb9d3cd8_1002.conda#b3c17d95b5a10c6e64a21fa17573e70e -https://conda.anaconda.org/conda-forge/linux-64/xorg-libxau-1.0.12-hb03c661_1.conda#b2895afaf55bf96a8c8282a2e47a5de0 -https://conda.anaconda.org/conda-forge/linux-64/xorg-libxdmcp-1.1.5-hb03c661_1.conda#1dafce8548e38671bea82e3f5c6ce22f -https://conda.anaconda.org/conda-forge/linux-64/libxcb-1.17.0-h8a09558_0.conda#92ed62436b625154323d40d5f2f11dd7 -https://conda.anaconda.org/conda-forge/noarch/markdown-3.10.2-pyhcf101f3_0.conda#ba0a9221ce1063f31692c07370d062f3 -https://conda.anaconda.org/conda-forge/noarch/mdurl-0.1.2-pyhd8ed1ab_1.conda#592132998493b3ff25fd7479396e8351 -https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-4.0.0-pyhd8ed1ab_0.conda#5b5203189eb668f042ac2b0826244964 -https://conda.anaconda.org/conda-forge/noarch/natsort-8.4.0-pyhcf101f3_2.conda#e941e85e273121222580723010bd4fa2 -https://conda.anaconda.org/conda-forge/noarch/packaging-26.1-pyhc364b38_0.conda#b8ae38639d323d808da535fb71e31be8 -https://conda.anaconda.org/conda-forge/linux-64/openjpeg-2.5.4-h55fea9a_0.conda#11b3379b191f63139e29c0d19dee24cd -https://conda.anaconda.org/conda-forge/linux-64/zlib-ng-2.3.3-hceb46e0_1.conda#2aadb0d17215603a82a2a6b0afd9a4cb -https://conda.anaconda.org/conda-forge/linux-64/pillow-12.2.0-py314h8ec4b1a_0.conda#76c4757c0ec9d11f969e8eb44899307b -https://conda.anaconda.org/conda-forge/noarch/narwhals-2.20.0-pyhcf101f3_0.conda#6cac1a50359219d786453c6fef819f98 -https://conda.anaconda.org/conda-forge/noarch/plotly-6.6.0-pyhd8ed1ab_0.conda#3e9427ee186846052e81fadde8ebe96a -https://conda.anaconda.org/conda-forge/linux-64/polars-runtime-32-1.40.0-py310hffdcd12_0.conda#8eacf9ff4d4e1ca1b52f8f3ba3e0c993 -https://conda.anaconda.org/conda-forge/noarch/polars-1.40.0-pyh58ad624_0.conda#fd16be490f5403adfbf27dd4901bbe34 -https://conda.anaconda.org/conda-forge/linux-64/polars-runtime-compat-1.40.0-py310hbcd5346_0.conda#03a6899e17bb731c8e21b08212f1a64c -https://conda.anaconda.org/conda-forge/noarch/polars-lts-cpu-1.34.0.deprecated-hc364b38_0.conda#ef0340e75068ac8ff96462749b5c98e7 -https://conda.anaconda.org/conda-forge/linux-64/yaml-0.2.5-h280c20c_3.conda#a77f85f77be52ff59391544bfe73390a -https://conda.anaconda.org/conda-forge/linux-64/pyyaml-6.0.3-py314h67df5f8_1.conda#2035f68f96be30dc60a5dfd7452c7941 -https://conda.anaconda.org/conda-forge/noarch/pyaml-env-1.2.2-pyhd8ed1ab_0.conda#e17be1016bcc3516827b836cd3e4d9dc -https://conda.anaconda.org/conda-forge/linux-64/pydantic-core-2.46.3-py314h2e6c369_0.conda#1f3fd537f929b8d3236f9f0f0e7f7a32 -https://conda.anaconda.org/conda-forge/noarch/typing-inspection-0.4.2-pyhd8ed1ab_1.conda#a0a4a3035667fc34f29bfbd5c190baa6 -https://conda.anaconda.org/conda-forge/noarch/pydantic-2.13.3-pyhcf101f3_0.conda#f690e6f204efd2e5c06b57518a383d98 -https://conda.anaconda.org/conda-forge/noarch/python-dotenv-1.2.2-pyhcf101f3_0.conda#130584ad9f3a513cdd71b1fdc1244e9c -https://conda.anaconda.org/conda-forge/noarch/python-kaleido-0.2.1-pyhd8ed1ab_0.tar.bz2#310259a5b03ff02289d7705f39e2b1d2 -https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda#461219d1a5bd61342293efa2c0c90eac -https://conda.anaconda.org/conda-forge/noarch/urllib3-2.6.3-pyhd8ed1ab_0.conda#9272daa869e03efe68833e3dc7a02130 -https://conda.anaconda.org/conda-forge/noarch/requests-2.33.1-pyhcf101f3_0.conda#10afbb4dbf06ff959ad25a92ccee6e59 -https://conda.anaconda.org/conda-forge/noarch/pygments-2.20.0-pyhd8ed1ab_0.conda#16c18772b340887160c79a6acc022db0 -https://conda.anaconda.org/conda-forge/noarch/rich-15.0.0-pyhcf101f3_0.conda#0242025a3c804966bf71aa04eee82f66 -https://conda.anaconda.org/conda-forge/noarch/rich-click-1.9.7-pyh8f84b5b_0.conda#0c20a8ebcddb24a45da89d5e917e6cb9 -https://conda.anaconda.org/conda-forge/noarch/spectra-0.0.11-pyhd8ed1ab_2.conda#472239e4eb7b5a84bb96b3ed7e3a596a -https://conda.anaconda.org/conda-forge/linux-64/regex-2026.4.4-py314h5bd0f2a_0.conda#4ffb42385183c854564f1f9adcf80a63 -https://conda.anaconda.org/conda-forge/linux-64/tiktoken-0.12.0-py314h67fec18_3.conda#d705f9d8a1185a2b01cced191177a028 -https://conda.anaconda.org/conda-forge/noarch/tqdm-4.67.3-pyh8f84b5b_0.conda#e5ce43272193b38c2e9037446c1d9206 -https://conda.anaconda.org/conda-forge/noarch/typeguard-4.5.1-pyhd8ed1ab_0.conda#260af1b0a94f719de76b4e14094e9a3b -https://conda.anaconda.org/bioconda/noarch/multiqc-1.34-pyhdfd78af_0.conda#a7111ab9a6a6146b40cbce16655ac873 -https://conda.anaconda.org/conda-forge/noarch/pip-26.0.1-pyh145f28c_0.conda#09a970fbf75e8ed1aa633827ded6aa4f -https://conda.anaconda.org/conda-forge/linux-64/procps-ng-4.0.6-h18c060e_0.conda#f2c23a77b25efcad57d377b34bd84941 diff --git a/modules/nf-core/multiqc/.conda-lock/linux_arm64-bd-40bf3b435e89dc22_1.txt b/modules/nf-core/multiqc/.conda-lock/linux_arm64-bd-5c84a5000a226ab5_1.txt similarity index 74% rename from modules/nf-core/multiqc/.conda-lock/linux_arm64-bd-40bf3b435e89dc22_1.txt rename to modules/nf-core/multiqc/.conda-lock/linux_arm64-bd-5c84a5000a226ab5_1.txt index a58231a..3d5b93d 100644 --- a/modules/nf-core/multiqc/.conda-lock/linux_arm64-bd-40bf3b435e89dc22_1.txt +++ b/modules/nf-core/multiqc/.conda-lock/linux_arm64-bd-5c84a5000a226ab5_1.txt @@ -14,120 +14,118 @@ linux-aarch64: - conda: https://conda.anaconda.org/conda-forge/noarch/_python_abi3_support-1.0-hd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.7.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/attrs-26.1.0-pyhcf101f3_0.conda -- conda: https://conda.anaconda.org/conda-forge/noarch/backports.zstd-1.3.0-py314h680f03e_0.conda +- conda: https://conda.anaconda.org/conda-forge/noarch/backports.zstd-1.5.0-py314h680f03e_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/brotli-python-1.2.0-py314h352cb57_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/bzip2-1.0.8-h4777abc_9.conda -- conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.2.25-hbd8a1cb_0.conda -- conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2026.2.25-pyhd8ed1ab_0.conda -- conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.6-pyhd8ed1ab_0.conda -- conda: https://conda.anaconda.org/conda-forge/noarch/click-8.3.1-pyh8f84b5b_1.conda +- conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.5.20-hbd8a1cb_0.conda +- conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2026.5.20-pyhd8ed1ab_0.conda +- conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.7-pyhd8ed1ab_0.conda +- conda: https://conda.anaconda.org/conda-forge/noarch/click-8.4.0-pyhc90fa1f_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/coloredlogs-15.0.1-pyhd8ed1ab_4.conda - conda: https://conda.anaconda.org/conda-forge/noarch/colormath-3.0.0-pyhd8ed1ab_4.conda -- conda: https://conda.anaconda.org/conda-forge/noarch/cpython-3.14.3-py314hd8ed1ab_101.conda -- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/expat-2.7.4-hfae3067_0.conda +- conda: https://conda.anaconda.org/conda-forge/noarch/cpython-3.14.5-py314hd8ed1ab_100.conda +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/expat-2.8.1-hfae3067_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-dejavu-sans-mono-2.37-hab24e00_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-inconsolata-3.000-h77eed37_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-source-code-pro-2.038-h77eed37_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-ubuntu-0.83-h77eed37_3.conda -- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/fontconfig-2.17.1-hba86a56_0.conda +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/fontconfig-2.18.0-hba86a56_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/fonts-conda-forge-1-hc364b38_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.3.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/hpack-4.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/humanfriendly-10.0-pyh707e725_8.conda - conda: https://conda.anaconda.org/conda-forge/noarch/humanize-4.15.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.1.0-pyhd8ed1ab_0.conda -- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/icu-78.3-hcab7f73_0.conda -- conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.11-pyhd8ed1ab_0.conda -- conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.8.0-pyhcf101f3_0.conda +- conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.15-pyhcf101f3_0.conda +- conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-9.0.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.6-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jsonschema-4.26.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jsonschema-specifications-2025.9.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/kaleido-core-0.2.1-he5a581e_0.tar.bz2 -- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/lcms2-2.18-h9d5b58d_0.conda +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/lcms2-2.19.1-h9d5b58d_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/ld_impl_linux-aarch64-2.45.1-default_h1979696_102.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/lerc-4.1.0-h52b7260_0.conda -- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libblas-3.11.0-5_haddc8a3_openblas.conda -- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libcblas-3.11.0-5_hd72aa62_openblas.conda +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libblas-3.11.0-7_haddc8a3_openblas.conda +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libcblas-3.11.0-7_hd72aa62_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libdeflate-1.25-h1af38f5_0.conda -- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libexpat-2.7.4-hfae3067_0.conda +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libexpat-2.8.1-hfae3067_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libffi-3.5.2-h376a255_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libfreetype-2.14.3-h8af1aa0_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libfreetype6-2.14.3-hdae7a39_0.conda -- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgcc-15.2.0-h8acb6b2_18.conda -- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgcc-ng-15.2.0-he9431aa_18.conda -- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgfortran-15.2.0-he9431aa_18.conda -- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgfortran5-15.2.0-h1b7bec0_18.conda -- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgomp-15.2.0-h8acb6b2_18.conda -- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libjpeg-turbo-3.1.2-he30d5cf_0.conda -- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/liblapack-3.11.0-5_h88aeb00_openblas.conda -- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/liblzma-5.8.2-he30d5cf_0.conda +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgcc-15.2.0-h8acb6b2_19.conda +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgcc-ng-15.2.0-he9431aa_19.conda +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgfortran-15.2.0-he9431aa_19.conda +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgfortran5-15.2.0-h1b7bec0_19.conda +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgomp-15.2.0-h8acb6b2_19.conda +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libjpeg-turbo-3.1.4.1-he30d5cf_0.conda +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/liblapack-3.11.0-7_h88aeb00_openblas.conda +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/liblzma-5.8.3-he30d5cf_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libmpdec-4.0.0-he30d5cf_1.conda -- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libopenblas-0.3.30-pthreads_h9d3fd7e_4.conda -- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libpng-1.6.55-h1abf092_0.conda -- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libsqlite-3.52.0-h10b116e_0.conda -- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libstdcxx-15.2.0-hef695bb_18.conda +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libopenblas-0.3.33-pthreads_h9d3fd7e_0.conda +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libpng-1.6.58-h1abf092_0.conda +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libsqlite-3.53.1-h022381a_0.conda +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libstdcxx-15.2.0-hef695bb_19.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libtiff-4.7.1-hdb009f0_1.conda -- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libuuid-2.41.3-h1022ec0_0.conda +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libuuid-2.42.1-h1022ec0_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libwebp-base-1.6.0-ha2e29f5_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libxcb-1.17.0-h262b8f6_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libzlib-1.3.2-hdc9db2a_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-3.10.2-pyhcf101f3_0.conda -- conda: https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-4.0.0-pyhd8ed1ab_0.conda +- conda: https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-4.2.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/markupsafe-3.0.3-py314hb76de3f_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/mathjax-2.7.7-h8af1aa0_3.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/mdurl-0.1.2-pyhd8ed1ab_1.conda -- conda: https://conda.anaconda.org/bioconda/noarch/multiqc-1.33-pyhdfd78af_0.conda -- conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-2.18.1-pyhcf101f3_1.conda +- conda: https://conda.anaconda.org/bioconda/noarch/multiqc-1.35-pyhdfd78af_1.conda +- conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-2.21.2-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/natsort-8.4.0-pyhcf101f3_2.conda -- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/ncurses-6.5-ha32ae93_3.conda +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/ncurses-6.6-hf8d1292_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/networkx-3.6.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/nspr-4.38-h3ad9384_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/nss-3.118-h544fa81_0.conda -- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/numpy-2.4.3-py314haac167e_0.conda +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/numpy-2.4.6-py314he1698a1_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/openjpeg-2.5.4-h5da879a_0.conda -- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/openssl-3.6.1-h546c87b_1.conda -- conda: https://conda.anaconda.org/conda-forge/noarch/packaging-26.0-pyhcf101f3_0.conda -- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/pillow-12.1.1-py314hac3e5ec_0.conda +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/openssl-3.6.2-h546c87b_0.conda +- conda: https://conda.anaconda.org/conda-forge/noarch/packaging-26.2-pyhc364b38_0.conda +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/pillow-12.2.0-py314hac3e5ec_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/plotly-6.6.0-pyhd8ed1ab_0.conda -- conda: https://conda.anaconda.org/conda-forge/noarch/polars-1.39.3-pyh58ad624_1.conda -- conda: https://conda.anaconda.org/conda-forge/noarch/polars-lts-cpu-1.34.0.deprecated-hc364b38_0.conda -- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/polars-runtime-32-1.39.3-py310hff09b76_1.conda -- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/polars-runtime-compat-1.39.3-py310hf00a4a2_1.conda +- conda: https://conda.anaconda.org/conda-forge/noarch/polars-1.41.0-pyh58ad624_0.conda +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/polars-runtime-32-1.41.0-py310h32c7c23_0.conda +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/polars-runtime-compat-1.41.0-py310hc0e61be_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/procps-ng-4.0.6-h1779866_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/pthread-stubs-0.4-h86ecc28_1002.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pyaml-env-1.2.2-pyhd8ed1ab_0.conda -- conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-2.12.5-pyhcf101f3_1.conda -- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/pydantic-core-2.41.5-py314h451b6cc_1.conda -- conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.19.2-pyhd8ed1ab_0.conda +- conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-2.13.4-pyhcf101f3_0.conda +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/pydantic-core-2.46.4-py314h451b6cc_0.conda +- conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.20.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda -- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/python-3.14.3-hb06a95a_101_cp314.conda +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/python-3.14.5-hfd9ac0a_100_cp314.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-dotenv-1.2.2-pyhcf101f3_0.conda -- conda: https://conda.anaconda.org/conda-forge/noarch/python-gil-3.14.3-h4df99d1_101.conda +- conda: https://conda.anaconda.org/conda-forge/noarch/python-gil-3.14.5-h4df99d1_100.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-kaleido-0.2.1-pyhd8ed1ab_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.14-8_cp314.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/pyyaml-6.0.3-py314h807365f_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/readline-8.3-hb682ff5_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/referencing-0.37.0-pyhcf101f3_0.conda -- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/regex-2026.2.28-py314h51f160d_0.conda -- conda: https://conda.anaconda.org/conda-forge/noarch/requests-2.32.5-pyhcf101f3_1.conda -- conda: https://conda.anaconda.org/conda-forge/noarch/rich-14.3.3-pyhcf101f3_0.conda +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/regex-2026.5.9-py314h51f160d_0.conda +- conda: https://conda.anaconda.org/conda-forge/noarch/requests-2.34.2-pyhcf101f3_0.conda +- conda: https://conda.anaconda.org/conda-forge/noarch/rich-15.0.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/rich-click-1.9.7-pyh8f84b5b_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/rpds-py-0.30.0-py314h02b7a91_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/spectra-0.0.11-pyhd8ed1ab_2.conda -- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/sqlite-3.52.0-hf1c7be2_0.conda +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/sqlite-3.53.1-he8854b5_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/tiktoken-0.12.0-py314h6a36e60_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/tk-8.6.13-noxft_h0dc03b3_103.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tqdm-4.67.3-pyh8f84b5b_0.conda -- conda: https://conda.anaconda.org/conda-forge/noarch/typeguard-4.5.1-pyhd8ed1ab_0.conda +- conda: https://conda.anaconda.org/conda-forge/noarch/typeguard-4.5.2-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.15.0-h396c80c_0.conda -- conda: https://conda.anaconda.org/conda-forge/noarch/typing-inspection-0.4.2-pyhd8ed1ab_1.conda +- conda: https://conda.anaconda.org/conda-forge/noarch/typing-inspection-0.4.2-pyhcf101f3_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025c-hc9c84f9_1.conda -- conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.6.3-pyhd8ed1ab_0.conda +- conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.7.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/xorg-libxau-1.0.12-he30d5cf_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/xorg-libxdmcp-1.1.5-he30d5cf_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/yaml-0.2.5-h80f16a2_3.conda -- conda: https://conda.anaconda.org/conda-forge/noarch/zipp-3.23.0-pyhcf101f3_1.conda +- conda: https://conda.anaconda.org/conda-forge/noarch/zipp-4.1.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/zlib-ng-2.3.3-ha7cb516_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/zstd-1.5.7-h85ac4a6_6.conda packages: @@ -173,15 +171,15 @@ license: MIT license_family: MIT size: 64927 timestamp: 1773935801332 -- conda: https://conda.anaconda.org/conda-forge/noarch/backports.zstd-1.3.0-py314h680f03e_0.conda +- conda: https://conda.anaconda.org/conda-forge/noarch/backports.zstd-1.5.0-py314h680f03e_0.conda noarch: generic -sha256: c31ab719d256bc6f89926131e88ecd0f0c5d003fe8481852c6424f4ec6c7eb29 -md5: a2ac7763a9ac75055b68f325d3255265 +sha256: a1c97297e867776760489537bc5ae36fa83a154be30e3b79385a39ca4cb058fe +md5: 1133126d840e75287d83947be3fc3e71 depends: - python >=3.14 license: BSD-3-Clause AND MIT AND EPL-2.0 -size: 7514 -timestamp: 1767044983590 +size: 7533 +timestamp: 1778594057496 - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/brotli-python-1.2.0-py314h352cb57_1.conda sha256: 5a5b0cdcd7ed89c6a8fb830924967f6314a2b71944bc1ebc2c105781ba97aa75 md5: a1b5c571a0923a205d663d8678df4792 @@ -206,42 +204,42 @@ license: bzip2-1.0.6 license_family: BSD size: 192412 timestamp: 1771350241232 -- conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.2.25-hbd8a1cb_0.conda -sha256: 67cc7101b36421c5913a1687ef1b99f85b5d6868da3abbf6ec1a4181e79782fc -md5: 4492fd26db29495f0ba23f146cd5638d +- conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.5.20-hbd8a1cb_0.conda +sha256: 9812a303a1395e1dafbd92e5bc8a1ff6013bcbba0a09c7f03a8d23e43560aa9b +md5: 489b8e97e666c93f68fdb35c3c9b957f depends: - __unix license: ISC -size: 147413 -timestamp: 1772006283803 -- conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2026.2.25-pyhd8ed1ab_0.conda -sha256: a6b118fd1ed6099dc4fc03f9c492b88882a780fadaef4ed4f93dc70757713656 -md5: 765c4d97e877cdbbb88ff33152b86125 +size: 129868 +timestamp: 1779289852439 +- conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2026.5.20-pyhd8ed1ab_0.conda +sha256: 645655a3510e38e625da136595f3f16f2130c3263630cc3bc8f60f619ddbe490 +md5: 9fefff2f745ea1cc2ef15211a20c054a depends: - python >=3.10 license: ISC -size: 151445 -timestamp: 1772001170301 -- conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.6-pyhd8ed1ab_0.conda -sha256: d86dfd428b2e3c364fa90e07437c8405d635aa4ef54b25ab51d9c712be4112a5 -md5: 49ee13eb9b8f44d63879c69b8a40a74b +size: 134201 +timestamp: 1779285131141 +- conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.7-pyhd8ed1ab_0.conda +sha256: 3f9483d62ce24ecd063f8a5a714448445dc8d9e201147c46699fc0033e824457 +md5: a9167b9571f3baa9d448faa2139d1089 depends: - python >=3.10 license: MIT license_family: MIT -size: 58510 -timestamp: 1773660086450 -- conda: https://conda.anaconda.org/conda-forge/noarch/click-8.3.1-pyh8f84b5b_1.conda -sha256: 38cfe1ee75b21a8361c8824f5544c3866f303af1762693a178266d7f198e8715 -md5: ea8a6c3256897cc31263de9f455e25d9 +size: 58872 +timestamp: 1775127203018 +- conda: https://conda.anaconda.org/conda-forge/noarch/click-8.4.0-pyhc90fa1f_0.conda +sha256: 99ab8ef815c4520cce3a7482c2513f377c14348206857661d84c76a55e030f97 +md5: 003767c47f1f0a474c4de268b57839c3 depends: -- python >=3.10 - __unix - python +- python >=3.10 license: BSD-3-Clause license_family: BSD -size: 97676 -timestamp: 1764518652276 +size: 104631 +timestamp: 1779108494556 - conda: https://conda.anaconda.org/conda-forge/noarch/coloredlogs-15.0.1-pyhd8ed1ab_4.conda sha256: 8021c76eeadbdd5784b881b165242db9449783e12ce26d6234060026fd6a8680 md5: b866ff7007b934d564961066c8195983 @@ -263,26 +261,26 @@ license: BSD-3-Clause license_family: BSD size: 39326 timestamp: 1735759976140 -- conda: https://conda.anaconda.org/conda-forge/noarch/cpython-3.14.3-py314hd8ed1ab_101.conda +- conda: https://conda.anaconda.org/conda-forge/noarch/cpython-3.14.5-py314hd8ed1ab_100.conda noarch: generic -sha256: 91b06300879df746214f7363d6c27c2489c80732e46a369eb2afc234bcafb44c -md5: 3bb89e4f795e5414addaa531d6b1500a +sha256: 777882d2685f368417f31bbe1b28f73687fc6c8f6a5768bda20ffeefa6b07f5b +md5: a749029ce5d0632a913db19d17f944ab depends: - python >=3.14,<3.15.0a0 - python_abi * *_cp314 license: Python-2.0 -size: 50078 -timestamp: 1770674447292 -- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/expat-2.7.4-hfae3067_0.conda -sha256: 5f087bef054c681edcaae84a8c2230585b938691e371ff92957a30707b7fcdf7 -md5: b304307db639831ad7caabd2eac6fca6 +size: 50212 +timestamp: 1779236682725 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/expat-2.8.1-hfae3067_0.conda +sha256: a9cd5eb1700e11cc39acc36630a2d72a4e317943bd7c5695cd8804419f04ff42 +md5: 89f0247b3cea528d8ad1a6664a313153 depends: -- libexpat 2.7.4 hfae3067_0 +- libexpat 2.8.1 hfae3067_0 - libgcc >=14 license: MIT license_family: MIT -size: 137701 -timestamp: 1771259543650 +size: 140114 +timestamp: 1779278679081 - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-dejavu-sans-mono-2.37-hab24e00_0.tar.bz2 sha256: 58d7f40d2940dd0a8aa28651239adbf5613254df0f75789919c4e6762054403b md5: 0c96522c6bdaed4b1566d11387caaf45 @@ -311,20 +309,20 @@ license: LicenseRef-Ubuntu-Font-Licence-Version-1.0 license_family: Other size: 1620504 timestamp: 1727511233259 -- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/fontconfig-2.17.1-hba86a56_0.conda -sha256: 835aff8615dd8d8fff377679710ce81b8a2c47b6404e21a92fb349fda193a15c -md5: 0fed1ff55f4938a65907f3ecf62609db +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/fontconfig-2.18.0-hba86a56_0.conda +sha256: 1805f4ab3d9e1734a5a17abccc2cb0fdade51d4d5f29bdc410600ea0115ec050 +md5: b660d59a9d0fb3297327418624acaec3 depends: -- libexpat >=2.7.4,<3.0a0 -- libfreetype >=2.14.1 -- libfreetype6 >=2.14.1 +- libexpat >=2.8.1,<3.0a0 +- libfreetype >=2.14.3 +- libfreetype6 >=2.14.3 - libgcc >=14 -- libuuid >=2.41.3,<3.0a0 -- libzlib >=1.3.1,<2.0a0 +- libuuid >=2.42.1,<3.0a0 +- libzlib >=1.3.2,<2.0a0 license: MIT license_family: MIT -size: 279044 -timestamp: 1771382728182 +size: 293348 +timestamp: 1779421661332 - conda: https://conda.anaconda.org/conda-forge/noarch/fonts-conda-forge-1-hc364b38_1.conda sha256: 54eea8469786bc2291cc40bca5f46438d3e062a399e8f53f013b6a9f50e98333 md5: a7970cd949a077b7cb9696379d338681 @@ -386,36 +384,26 @@ license: MIT license_family: MIT size: 17397 timestamp: 1737618427549 -- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/icu-78.3-hcab7f73_0.conda -sha256: 49ba6aed2c6b482bb0ba41078057555d29764299bc947b990708617712ef6406 -md5: 546da38c2fa9efacf203e2ad3f987c59 -depends: -- libgcc >=14 -- libstdcxx >=14 -license: MIT -license_family: MIT -size: 12837286 -timestamp: 1773822650615 -- conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.11-pyhd8ed1ab_0.conda -sha256: ae89d0299ada2a3162c2614a9d26557a92aa6a77120ce142f8e0109bbf0342b0 -md5: 53abe63df7e10a6ba605dc5f9f961d36 +- conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.15-pyhcf101f3_0.conda +sha256: 3d25f9f6f7ab3e1ce6429fc8c8aae0335cf446692e715068488536d220cc43de +md5: 1b9083b7f00609605d1483dbc6071a81 depends: - python >=3.10 +- python license: BSD-3-Clause license_family: BSD -size: 50721 -timestamp: 1760286526795 -- conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.8.0-pyhcf101f3_0.conda -sha256: 82ab2a0d91ca1e7e63ab6a4939356667ef683905dea631bc2121aa534d347b16 -md5: 080594bf4493e6bae2607e65390c520a +size: 62642 +timestamp: 1779294335905 +- conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-9.0.0-pyhcf101f3_0.conda +sha256: 43e2a5497cad1598ff88a3e69f69bc88b7b8f141fa63c60eab5db296317318b8 +md5: ffc17e785d64e12fc311af9184221839 depends: - python >=3.10 - zipp >=3.20 - python license: Apache-2.0 -license_family: APACHE -size: 34387 -timestamp: 1773931568510 +size: 34766 +timestamp: 1779714582554 - conda: https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.6-pyhcf101f3_1.conda sha256: fc9ca7348a4f25fed2079f2153ecdcf5f9cf2a0bc36c4172420ca09e1849df7b md5: 04558c96691bed63104678757beb4f8d @@ -469,17 +457,17 @@ license: MIT license_family: MIT size: 65750397 timestamp: 1615199465742 -- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/lcms2-2.18-h9d5b58d_0.conda -sha256: 379ef5e91a587137391a6149755d0e929f1a007d2dcb211318ac670a46c8596f -md5: bb960f01525b5e001608afef9d47b79c +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/lcms2-2.19.1-h9d5b58d_0.conda +sha256: 1e5f68e4b36a0e1a278c6dc026bc3d7775518a15832cbc9d7fc1c0e4c47784b1 +md5: b1f8bee3c53a6d2c103fb4a1ae44f5c4 depends: - libgcc >=14 -- libjpeg-turbo >=3.1.2,<4.0a0 +- libjpeg-turbo >=3.1.4.1,<4.0a0 - libtiff >=4.7.1,<4.8.0a0 license: MIT license_family: MIT -size: 293039 -timestamp: 1768184778398 +size: 296899 +timestamp: 1778079402392 - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/ld_impl_linux-aarch64-2.45.1-default_h1979696_102.conda sha256: 7abd913d81a9bf00abb699e8987966baa2065f5132e37e815f92d90fc6bba530 md5: a21644fc4a83da26452a718dc9468d5f @@ -501,37 +489,37 @@ license: Apache-2.0 license_family: Apache size: 240444 timestamp: 1773114901155 -- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libblas-3.11.0-5_haddc8a3_openblas.conda -build_number: 5 -sha256: 700f3c03d0fba8e687a345404a45fbabe781c1cf92242382f62cef2948745ec4 -md5: 5afcea37a46f76ec1322943b3c4dfdc0 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libblas-3.11.0-7_haddc8a3_openblas.conda +build_number: 7 +sha256: f27ba323c2f1e1731b5e880fe520f178f55047f25be94f77e649605b2343c066 +md5: e8d07b777f6ff1fab69665336561910b depends: -- libopenblas >=0.3.30,<0.3.31.0a0 -- libopenblas >=0.3.30,<1.0a0 +- libopenblas >=0.3.33,<0.3.34.0a0 +- libopenblas >=0.3.33,<1.0a0 constrains: -- mkl <2026 -- libcblas 3.11.0 5*_openblas -- liblapack 3.11.0 5*_openblas -- liblapacke 3.11.0 5*_openblas -- blas 2.305 openblas +- liblapack 3.11.0 7*_openblas +- libcblas 3.11.0 7*_openblas +- mkl <2027 +- blas 2.307 openblas +- liblapacke 3.11.0 7*_openblas license: BSD-3-Clause license_family: BSD -size: 18369 -timestamp: 1765818610617 -- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libcblas-3.11.0-5_hd72aa62_openblas.conda -build_number: 5 -sha256: 3fad5c9de161dccb4e42c8b1ae8eccb33f4ed56bccbcced9cbb0956ae7869e61 -md5: 0b2f1143ae2d0aa4c991959d0daaf256 -depends: -- libblas 3.11.0 5_haddc8a3_openblas +size: 18696 +timestamp: 1778489796402 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libcblas-3.11.0-7_hd72aa62_openblas.conda +build_number: 7 +sha256: c8f0192362966df0828419f042d6f94c079e5df00ad6bd05b5e84c12b42f8cc7 +md5: 90ac57b82c055faa9be25031864b7d8f +depends: +- libblas 3.11.0 7_haddc8a3_openblas constrains: -- liblapack 3.11.0 5*_openblas -- liblapacke 3.11.0 5*_openblas -- blas 2.305 openblas +- liblapack 3.11.0 7*_openblas +- blas 2.307 openblas +- liblapacke 3.11.0 7*_openblas license: BSD-3-Clause license_family: BSD -size: 18371 -timestamp: 1765818618899 +size: 18664 +timestamp: 1778489802790 - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libdeflate-1.25-h1af38f5_0.conda sha256: 48814b73bd462da6eed2e697e30c060ae16af21e9fbed30d64feaf0aad9da392 md5: a9138815598fe6b91a1d6782ca657b0c @@ -541,17 +529,17 @@ license: MIT license_family: MIT size: 71117 timestamp: 1761979776756 -- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libexpat-2.7.4-hfae3067_0.conda -sha256: 995ce3ad96d0f4b5ed6296b051a0d7b6377718f325bc0e792fbb96b0e369dad7 -md5: 57f3b3da02a50a1be2a6fe847515417d +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libexpat-2.8.1-hfae3067_0.conda +sha256: 1fc392b997c6ee2bd3226a7cd870d0edbcbb367e25f9f18dd4a7025fced6efc0 +md5: 513dd884361dfb8a554298ed69b58823 depends: - libgcc >=14 constrains: -- expat 2.7.4.* +- expat 2.8.1.* license: MIT license_family: MIT -size: 76564 -timestamp: 1771259530958 +size: 77140 +timestamp: 1779278671302 - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libffi-3.5.2-h376a255_0.conda sha256: 3df4c539449aabc3443bbe8c492c01d401eea894603087fca2917aa4e1c2dea9 md5: 2f364feefb6a7c00423e80dcb12db62a @@ -581,90 +569,90 @@ constrains: license: GPL-2.0-only OR FTL size: 422941 timestamp: 1774301093473 -- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgcc-15.2.0-h8acb6b2_18.conda -sha256: 43df385bedc1cab11993c4369e1f3b04b4ca5d0ea16cba6a0e7f18dbc129fcc9 -md5: 552567ea2b61e3a3035759b2fdb3f9a6 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgcc-15.2.0-h8acb6b2_19.conda +sha256: 4592b096e553f67799ae70d4b6167eeda3ec74587d68c7aecbf4e7b1df136681 +md5: f35b3f52d0a2ec4ffe3c89ba135cdb9a depends: - _openmp_mutex >=4.5 constrains: -- libgcc-ng ==15.2.0=*_18 -- libgomp 15.2.0 h8acb6b2_18 +- libgomp 15.2.0 h8acb6b2_19 +- libgcc-ng ==15.2.0=*_19 license: GPL-3.0-only WITH GCC-exception-3.1 license_family: GPL -size: 622900 -timestamp: 1771378128706 -- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgcc-ng-15.2.0-he9431aa_18.conda -sha256: 83bb0415f59634dccfa8335d4163d1f6db00a27b36666736f9842b650b92cf2f -md5: 4feebd0fbf61075a1a9c2e9b3936c257 +size: 622462 +timestamp: 1778268755949 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgcc-ng-15.2.0-he9431aa_19.conda +sha256: 1137f93f477f56199ded24117430045a0c02cbe8b10031beac3b9ad2138539d3 +md5: 770cf892e5530f43e63cadc673e85653 depends: -- libgcc 15.2.0 h8acb6b2_18 +- libgcc 15.2.0 h8acb6b2_19 license: GPL-3.0-only WITH GCC-exception-3.1 license_family: GPL -size: 27568 -timestamp: 1771378136019 -- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgfortran-15.2.0-he9431aa_18.conda -sha256: 7dcd7dff2505d56fd5272a6e712ec912f50a46bf07dc6873a7e853694304e6e4 -md5: 41f261f5e4e2e8cbd236c2f1f15dae1b +size: 27738 +timestamp: 1778268759211 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgfortran-15.2.0-he9431aa_19.conda +sha256: e5ad94be72634233510b33ba792a3339921bd468f0b8bc6961ea05eded251d9b +md5: c7a5b5decf969ead5ecada83654164cf depends: -- libgfortran5 15.2.0 h1b7bec0_18 +- libgfortran5 15.2.0 h1b7bec0_19 constrains: -- libgfortran-ng ==15.2.0=*_18 +- libgfortran-ng ==15.2.0=*_19 license: GPL-3.0-only WITH GCC-exception-3.1 license_family: GPL -size: 27587 -timestamp: 1771378169244 -- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgfortran5-15.2.0-h1b7bec0_18.conda -sha256: 85347670dfb4a8d4c13cd7cae54138dcf2b1606b6bede42eef5507bf5f9660c6 -md5: 574d88ce3348331e962cfa5ed451b247 +size: 27728 +timestamp: 1778268784621 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgfortran5-15.2.0-h1b7bec0_19.conda +sha256: af8e9bdcaa77f133a8ee4c1ef57ef564d9c45aa262abf9f5ef9b50eb99d96407 +md5: 779dbb494de6d3d6477cab52eb34285a depends: - libgcc >=15.2.0 constrains: - libgfortran 15.2.0 license: GPL-3.0-only WITH GCC-exception-3.1 license_family: GPL -size: 1486341 -timestamp: 1771378148102 -- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgomp-15.2.0-h8acb6b2_18.conda -sha256: fc716f11a6a8525e27a5d332ef6a689210b0d2a4dd1133edc0f530659aa9faa6 -md5: 4faa39bf919939602e594253bd673958 +size: 1487244 +timestamp: 1778268767295 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgomp-15.2.0-h8acb6b2_19.conda +sha256: 2370ef0ffcbae5bede3c4bf136add4abc257245eb91f724c99bb4a43116c5a83 +md5: c5e8a379c4a2ec2aea4ba22758c001d9 license: GPL-3.0-only WITH GCC-exception-3.1 license_family: GPL -size: 588060 -timestamp: 1771378040807 -- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libjpeg-turbo-3.1.2-he30d5cf_0.conda -sha256: 84064c7c53a64291a585d7215fe95ec42df74203a5bf7615d33d49a3b0f08bb6 -md5: 5109d7f837a3dfdf5c60f60e311b041f +size: 587387 +timestamp: 1778268674393 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libjpeg-turbo-3.1.4.1-he30d5cf_0.conda +sha256: e97ec2af5f09f8f6ea8ecd550055c95ae80fae22015fcfadaa94eafe025c9ccc +md5: a85ba48648f6868016f2741fd9170250 depends: - libgcc >=14 constrains: - jpeg <0.0.0a license: IJG AND BSD-3-Clause AND Zlib -size: 691818 -timestamp: 1762094728337 -- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/liblapack-3.11.0-5_h88aeb00_openblas.conda -build_number: 5 -sha256: 692222d186d3ffbc99eaf04b5b20181fd26aee1edec1106435a0a755c57cce86 -md5: 88d1e4133d1182522b403e9ba7435f04 -depends: -- libblas 3.11.0 5_haddc8a3_openblas +size: 693143 +timestamp: 1775962625956 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/liblapack-3.11.0-7_h88aeb00_openblas.conda +build_number: 7 +sha256: 20b38a0156200ac65f597bf0a93914c565435f2cc58b1042581854231a99ac35 +md5: 5899cbd743cc74fd655c1ed2af7120f3 +depends: +- libblas 3.11.0 7_haddc8a3_openblas constrains: -- liblapacke 3.11.0 5*_openblas -- blas 2.305 openblas -- libcblas 3.11.0 5*_openblas +- libcblas 3.11.0 7*_openblas +- blas 2.307 openblas +- liblapacke 3.11.0 7*_openblas license: BSD-3-Clause license_family: BSD -size: 18392 -timestamp: 1765818627104 -- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/liblzma-5.8.2-he30d5cf_0.conda -sha256: 843c46e20519651a3e357a8928352b16c5b94f4cd3d5481acc48be2e93e8f6a3 -md5: 96944e3c92386a12755b94619bae0b35 +size: 18685 +timestamp: 1778489809140 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/liblzma-5.8.3-he30d5cf_0.conda +sha256: d61962b9cd54c3554361550203c64d5b65b71e3058a285b66e4b04b9769f0a5c +md5: 76298a9e6d71ee6e832a8d0d7373b261 depends: - libgcc >=14 constrains: -- xz 5.8.2.* +- xz 5.8.3.* license: 0BSD -size: 125916 -timestamp: 1768754941722 +size: 126102 +timestamp: 1775828008518 - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libmpdec-4.0.0-he30d5cf_1.conda sha256: 57c0dd12d506e84541c4e877898bd2a59cca141df493d34036f18b2751e0a453 md5: 7b9813e885482e3ccb1fa212b86d7fd0 @@ -674,49 +662,48 @@ license: BSD-2-Clause license_family: BSD size: 114056 timestamp: 1769482343003 -- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libopenblas-0.3.30-pthreads_h9d3fd7e_4.conda -sha256: 794a7270ea049ec931537874cd8d2de0ef4b3cef71c055cfd8b4be6d2f4228b0 -md5: 11d7d57b7bdd01da745bbf2b67020b2e +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libopenblas-0.3.33-pthreads_h9d3fd7e_0.conda +sha256: b018ecfb05e75a8eea3f21f6b5c5c2a54b5178bdcf19e2e2df2735740214a8c8 +md5: 58a66cd95e9692f08abe89f55a6f3f12 depends: - libgcc >=14 - libgfortran - libgfortran5 >=14.3.0 constrains: -- openblas >=0.3.30,<0.3.31.0a0 +- openblas >=0.3.33,<0.3.34.0a0 license: BSD-3-Clause license_family: BSD -size: 4959359 -timestamp: 1763114173544 -- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libpng-1.6.55-h1abf092_0.conda -sha256: c7378c6b79de4d571d00ad1caf0a4c19d43c9c94077a761abb6ead44d891f907 -md5: be4088903b94ea297975689b3c3aeb27 +size: 5121336 +timestamp: 1776993423004 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libpng-1.6.58-h1abf092_0.conda +sha256: 483eaa53da40a6a3e558709d9f7b1ca388735364ae21a1ba58cf942514649c92 +md5: f51503ac45a4888bce71af9027a2ecc9 depends: - libgcc >=14 -- libzlib >=1.3.1,<2.0a0 +- libzlib >=1.3.2,<2.0a0 license: zlib-acknowledgement -size: 340156 -timestamp: 1770691477245 -- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libsqlite-3.52.0-h10b116e_0.conda -sha256: 1ddaf91b44fae83856276f4cb7ce544ffe41d4b55c1e346b504c6b45f19098d6 -md5: 77891484f18eca74b8ad83694da9815e +size: 341202 +timestamp: 1776315188425 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libsqlite-3.53.1-h022381a_0.conda +sha256: ad03b7d8e4d08001f0df88ee7a56108bb35bae4795a42b9a04cc1abfa822bd07 +md5: 2ec1119217d8f0d086e9a62f3cb0e5ea depends: -- icu >=78.2,<79.0a0 - libgcc >=14 -- libzlib >=1.3.1,<2.0a0 +- libzlib >=1.3.2,<2.0a0 license: blessing -size: 952296 -timestamp: 1772818881550 -- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libstdcxx-15.2.0-hef695bb_18.conda -sha256: 31fdb9ffafad106a213192d8319b9f810e05abca9c5436b60e507afb35a6bc40 -md5: f56573d05e3b735cb03efeb64a15f388 +size: 955361 +timestamp: 1777986487553 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libstdcxx-15.2.0-hef695bb_19.conda +sha256: 1dadc45e599f510dd5f97141dddcdbb9844d9f1430c1f3a38075cf1c58f87b4e +md5: 543fbc8d71f2a0baf04cf88ce96cb8bb depends: -- libgcc 15.2.0 h8acb6b2_18 +- libgcc 15.2.0 h8acb6b2_19 constrains: -- libstdcxx-ng ==15.2.0=*_18 +- libstdcxx-ng ==15.2.0=*_19 license: GPL-3.0-only WITH GCC-exception-3.1 license_family: GPL -size: 5541411 -timestamp: 1771378162499 +size: 5546559 +timestamp: 1778268777463 - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libtiff-4.7.1-hdb009f0_1.conda sha256: 7ff79470db39e803e21b8185bc8f19c460666d5557b1378d1b1e857d929c6b39 md5: 8c6fd84f9c87ac00636007c6131e457d @@ -733,15 +720,15 @@ depends: license: HPND size: 488407 timestamp: 1762022048105 -- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libuuid-2.41.3-h1022ec0_0.conda -sha256: c37a8e89b700646f3252608f8368e7eb8e2a44886b92776e57ad7601fc402a11 -md5: cf2861212053d05f27ec49c3784ff8bb +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libuuid-2.42.1-h1022ec0_0.conda +sha256: 1628839b062e98b2192857d4da8496ac9ac6b0dbb77aa040c34efc9192c440ee +md5: 0f42f9fedd2a32d798de95a7f65c456f depends: - libgcc >=14 license: BSD-3-Clause license_family: BSD size: 43453 -timestamp: 1766271546875 +timestamp: 1779118526838 - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libwebp-base-1.6.0-ha2e29f5_0.conda sha256: b03700a1f741554e8e5712f9b06dd67e76f5301292958cd3cb1ac8c6fdd9ed25 md5: 24e92d0942c799db387f5c9d7b81f1af @@ -785,16 +772,16 @@ license: BSD-3-Clause license_family: BSD size: 85893 timestamp: 1770694658918 -- conda: https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-4.0.0-pyhd8ed1ab_0.conda -sha256: 7b1da4b5c40385791dbc3cc85ceea9fad5da680a27d5d3cb8bfaa185e304a89e -md5: 5b5203189eb668f042ac2b0826244964 +- conda: https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-4.2.0-pyhd8ed1ab_0.conda +sha256: 0c4c35376fe920714390d46e4b8d31c876d65f18e1655899e0763ec25f2a902f +md5: 6d03368f2b2b0a5fb6839df53b2eb5e0 depends: - mdurl >=0.1,<1 - python >=3.10 license: MIT license_family: MIT -size: 64736 -timestamp: 1754951288511 +size: 69017 +timestamp: 1778169663339 - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/markupsafe-3.0.3-py314hb76de3f_1.conda sha256: 383c188496d13a55658c06e61e7d4cdff2c9f9d5a0648769fca8250bece7e0ef md5: e5de3c36dd548b35ff2a8aa49208dcb3 @@ -824,9 +811,9 @@ license: MIT license_family: MIT size: 14465 timestamp: 1733255681319 -- conda: https://conda.anaconda.org/bioconda/noarch/multiqc-1.33-pyhdfd78af_0.conda -sha256: f005760b13093362fc9c997d603dd487de32ab2e821a3cbce52a42bcb8136517 -md5: 698a8a27c2b9d8a542c70cb47099a75e +- conda: https://conda.anaconda.org/bioconda/noarch/multiqc-1.35-pyhdfd78af_1.conda +sha256: e86033aa55a9e915e2d0957e770bdb81e3feb26a227d1adb17f9d6c528da6a71 +md5: cdb20309681ba3ce8f52c110e214d4f3 depends: - click - coloredlogs @@ -840,10 +827,11 @@ depends: - packaging - pillow >=10.2.0 - plotly >=5.18 -- polars-lts-cpu +- polars >=1.34.0 +- polars-runtime-compat >=1.34.0 - pyaml-env - pydantic >=2.7.1 -- python >=3.8,!=3.14.1 +- python >=3.9,!=3.14.1 - python-dotenv - python-kaleido 0.2.1 - pyyaml >=4 @@ -853,20 +841,21 @@ depends: - spectra >=0.0.10 - tiktoken - tqdm -- typeguard +- typeguard >=4 license: GPL-3.0-or-later license_family: GPL3 -size: 4198799 -timestamp: 1765300743879 -- conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-2.18.1-pyhcf101f3_1.conda -sha256: 541fd4390a0687228b8578247f1536a821d9261389a65585af9d1a6f2a14e1e0 -md5: 30bec5e8f4c3969e2b1bd407c5e52afb +size: 4282188 +timestamp: 1779465338806 +- conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-2.21.2-pyhcf101f3_0.conda +sha256: 70f43d62450927d51673eecd8823e14f5b3cfebdb43cda1d502eba97162bab42 +md5: 6687827c332121727ce383919e1ec8c2 depends: - python >=3.10 - python license: MIT -size: 280459 -timestamp: 1774380620329 +license_family: MIT +size: 284323 +timestamp: 1778929680962 - conda: https://conda.anaconda.org/conda-forge/noarch/natsort-8.4.0-pyhcf101f3_2.conda sha256: aeb1548eb72e4f198e72f19d242fb695b35add2ac7b2c00e0d83687052867680 md5: e941e85e273121222580723010bd4fa2 @@ -877,14 +866,14 @@ license: MIT license_family: MIT size: 39262 timestamp: 1770905275632 -- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/ncurses-6.5-ha32ae93_3.conda -sha256: 91cfb655a68b0353b2833521dc919188db3d8a7f4c64bea2c6a7557b24747468 -md5: 182afabe009dc78d8b73100255ee6868 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/ncurses-6.6-hf8d1292_0.conda +sha256: 369db85c5cd8d99dde364ce70725d76511d9c8199e5b820c740414091bf5bcca +md5: b2a43456aa56fe80c2477a5094899eff depends: -- libgcc >=13 +- libgcc >=14 license: X11 AND BSD-3-Clause -size: 926034 -timestamp: 1738196018799 +size: 960036 +timestamp: 1777422174534 - conda: https://conda.anaconda.org/conda-forge/noarch/networkx-3.6.1-pyhcf101f3_0.conda sha256: f6a82172afc50e54741f6f84527ef10424326611503c64e359e25a19a8e4c1c6 md5: a2c1eeadae7a309daed9d62c96012a2b @@ -923,24 +912,23 @@ license: MPL-2.0 license_family: MOZILLA size: 2061869 timestamp: 1763490303490 -- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/numpy-2.4.3-py314haac167e_0.conda -sha256: a6d42fd88afc57c3b0a57b21a12eff7492dfc419bb61ee3f74e9ba6261dabc88 -md5: 25d896c331481145720a21e5145fad65 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/numpy-2.4.6-py314he1698a1_0.conda +sha256: 04af718b911f8a3a0095481c7e283aa081a175fe626eccbc2c5644bcb2aba9a1 +md5: 8b173772deea177b45d2a133b509b3f7 depends: - python -- libgcc >=14 -- python 3.14.* *_cp314 - libstdcxx >=14 -- libcblas >=3.9.0,<4.0a0 -- liblapack >=3.9.0,<4.0a0 +- libgcc >=14 - python_abi 3.14.* *_cp314 - libblas >=3.9.0,<4.0a0 +- liblapack >=3.9.0,<4.0a0 +- libcblas >=3.9.0,<4.0a0 constrains: - numpy-base <0a0 license: BSD-3-Clause license_family: BSD -size: 8008045 -timestamp: 1773839355275 +size: 8002900 +timestamp: 1779169206742 - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/openjpeg-2.5.4-h5da879a_0.conda sha256: bd1bc8bdde5e6c5cbac42d462b939694e40b59be6d0698f668515908640c77b8 md5: cea962410e327262346d48d01f05936c @@ -954,47 +942,47 @@ license: BSD-2-Clause license_family: BSD size: 392636 timestamp: 1758489353577 -- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/openssl-3.6.1-h546c87b_1.conda -sha256: 7f8048c0e75b2620254218d72b4ae7f14136f1981c5eb555ef61645a9344505f -md5: 25f5885f11e8b1f075bccf4a2da91c60 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/openssl-3.6.2-h546c87b_0.conda +sha256: 348cb74c1530ac241215d047ef65d134cf797af935c97a68655319362b7e6a01 +md5: 3b129669089e4d6a5c6871dbb4669b99 depends: - ca-certificates - libgcc >=14 license: Apache-2.0 license_family: Apache -size: 3692030 -timestamp: 1769557678657 -- conda: https://conda.anaconda.org/conda-forge/noarch/packaging-26.0-pyhcf101f3_0.conda -sha256: c1fc0f953048f743385d31c468b4a678b3ad20caffdeaa94bed85ba63049fd58 -md5: b76541e68fea4d511b1ac46a28dcd2c6 +size: 3706406 +timestamp: 1775589602258 +- conda: https://conda.anaconda.org/conda-forge/noarch/packaging-26.2-pyhc364b38_0.conda +sha256: 3906abfb6511a3bb309e39b9b1b7bc38f50a723971de2395489fd1f379255890 +md5: 4c06a92e74452cfa53623a81592e8934 depends: - python >=3.8 - python license: Apache-2.0 license_family: APACHE -size: 72010 -timestamp: 1769093650580 -- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/pillow-12.1.1-py314hac3e5ec_0.conda -sha256: 1ca2d1616baad9bccb7ebc425ef2dcd6cebe742fbe91edf226fb606ad371ca0f -md5: d3c959c7efe560b2d7da459d69121fe9 +size: 91574 +timestamp: 1777103621679 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/pillow-12.2.0-py314hac3e5ec_0.conda +sha256: 96b26c2657275ffe84ab510edf0865e21999d791485d12794edd4a71b837beb6 +md5: 87d58d103b47c4a8567b3d7666647684 depends: - python -- python 3.14.* *_cp314 - libgcc >=14 -- zlib-ng >=2.3.3,<2.4.0a0 +- python 3.14.* *_cp314 +- openjpeg >=2.5.4,<3.0a0 +- libxcb >=1.17.0,<2.0a0 - libwebp-base >=1.6.0,<2.0a0 +- zlib-ng >=2.3.3,<2.4.0a0 +- python_abi 3.14.* *_cp314 +- lcms2 >=2.18,<3.0a0 - tk >=8.6.13,<8.7.0a0 -- libfreetype >=2.14.1 -- libfreetype6 >=2.14.1 - libtiff >=4.7.1,<4.8.0a0 -- lcms2 >=2.18,<3.0a0 -- python_abi 3.14.* *_cp314 -- openjpeg >=2.5.4,<3.0a0 - libjpeg-turbo >=3.1.2,<4.0a0 -- libxcb >=1.17.0,<2.0a0 +- libfreetype >=2.14.3 +- libfreetype6 >=2.14.3 license: HPND -size: 1051828 -timestamp: 1770794010335 +size: 1062080 +timestamp: 1775060067775 - conda: https://conda.anaconda.org/conda-forge/noarch/plotly-6.6.0-pyhd8ed1ab_0.conda sha256: c418d325359fc7a0074cea7f081ef1bce26e114d2da8a0154c5d27ecc87a08e7 md5: 3e9427ee186846052e81fadde8ebe96a @@ -1008,11 +996,11 @@ license: MIT license_family: MIT size: 5251872 timestamp: 1772628857717 -- conda: https://conda.anaconda.org/conda-forge/noarch/polars-1.39.3-pyh58ad624_1.conda -sha256: d332c2d5002fc440ae37ed9679ffc21b552f18d20232390005d1dd3bce0888d3 -md5: d5a4e013a30dd8dfde9ab39f45aaf9c1 +- conda: https://conda.anaconda.org/conda-forge/noarch/polars-1.41.0-pyh58ad624_0.conda +sha256: 70fc56877c4a095ee658d61924d8019768fbae4a48437058d181fc94b0a7c4d8 +md5: 25a883fed9f1f3f21ff317a3e7c92ac4 depends: -- polars-runtime-32 ==1.39.3 +- polars-runtime-32 ==1.41.0 - python >=3.10 - python constrains: @@ -1026,27 +1014,16 @@ constrains: - pyiceberg >=0.7.1 - altair >=5.4.0 - great_tables >=0.8.0 -- polars-runtime-32 ==1.39.3 -- polars-runtime-64 ==1.39.3 -- polars-runtime-compat ==1.39.3 +- polars-runtime-32 ==1.41.0 +- polars-runtime-64 ==1.41.0 +- polars-runtime-compat ==1.41.0 license: MIT -license_family: MIT -size: 533495 -timestamp: 1774207987966 -- conda: https://conda.anaconda.org/conda-forge/noarch/polars-lts-cpu-1.34.0.deprecated-hc364b38_0.conda -sha256: e466fb31f67ba9bde18deafeb34263ca5eb25807f39ead0e9d753a8e82c4c4f4 -md5: ef0340e75068ac8ff96462749b5c98e7 -depends: -- polars >=1.34.0 -- polars-runtime-compat >=1.34.0 -license: MIT -license_family: MIT -size: 3902 -timestamp: 1760206808444 -- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/polars-runtime-32-1.39.3-py310hff09b76_1.conda +size: 539656 +timestamp: 1779630790562 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/polars-runtime-32-1.41.0-py310h32c7c23_0.conda noarch: python -sha256: c070be507c5a90df397a47ae0299660be437d5546d68f1bc0fa4402c9f07d59e -md5: 3c1a7c6b4ba8b9fb773ace9723f8a5db +sha256: d903b774ec09189e164207328aac157eee82fed8cc5c9ace46aeb5d1c15cb5b3 +md5: 8c08c506ed1ea8ce0ca37af5e918c58d depends: - python - libgcc >=14 @@ -1056,25 +1033,23 @@ depends: constrains: - __glibc >=2.17 license: MIT -license_family: MIT -size: 34785466 -timestamp: 1774207998285 -- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/polars-runtime-compat-1.39.3-py310hf00a4a2_1.conda +size: 38704429 +timestamp: 1779630794932 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/polars-runtime-compat-1.41.0-py310hc0e61be_0.conda noarch: python -sha256: 683315f1a49e47ce72bf9462419733b40b588b2b3106552d95fd4cd994e174de -md5: dd3464e2132dc3a783e76e5078870c76 +sha256: 101696adff43a654146376c62ef9611bf7946b95fa46f604fe247d77eefc6267 +md5: 65b73e4260677ee5162bdbb252e28e06 depends: - python -- libgcc >=14 - libstdcxx >=14 +- libgcc >=14 - _python_abi3_support 1.* - cpython >=3.10 constrains: - __glibc >=2.17 license: MIT -license_family: MIT -size: 34652491 -timestamp: 1774207996879 +size: 38651498 +timestamp: 1779630714016 - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/procps-ng-4.0.6-h1779866_0.conda sha256: e9cbcbc94e151ada3d6dc365380aaaf591f65012c16d9a2abaea4b9b90adc402 md5: ab7288cc39545556d1bc5e71ab2df9a9 @@ -1104,24 +1079,23 @@ license: MIT license_family: MIT size: 14645 timestamp: 1736766960536 -- conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-2.12.5-pyhcf101f3_1.conda -sha256: 868569d9505b7fe246c880c11e2c44924d7613a8cdcc1f6ef85d5375e892f13d -md5: c3946ed24acdb28db1b5d63321dbca7d +- conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-2.13.4-pyhcf101f3_0.conda +sha256: 69700e31165df070e9716315e042196aa92525dae5deb5107785847ab9f4189f +md5: 729843edafc0899b3348bd3f19525b9d depends: - typing-inspection >=0.4.2 - typing_extensions >=4.14.1 - python >=3.10 -- typing-extensions >=4.6.1 - annotated-types >=0.6.0 -- pydantic-core ==2.41.5 +- pydantic-core ==2.46.4 - python license: MIT license_family: MIT -size: 340482 -timestamp: 1764434463101 -- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/pydantic-core-2.41.5-py314h451b6cc_1.conda -sha256: f8acb2d03ebe80fed0032b9a989fc9acfb6735e3cd3f8c704b72728cb31868f6 -md5: 28f5027a1e04d67aa13fac1c5ba79693 +size: 346511 +timestamp: 1778103405862 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/pydantic-core-2.46.4-py314h451b6cc_0.conda +sha256: 1a7c6b18e404c13c4d959888ecb48a9ed9de0e41be2872932b83a35278088df0 +md5: 9c3ace6aba6df14b943256095ac1281e depends: - python - typing-extensions >=4.6.0,!=4.7.0 @@ -1132,17 +1106,17 @@ constrains: - __glibc >=2.17 license: MIT license_family: MIT -size: 1828339 -timestamp: 1762989038561 -- conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.19.2-pyhd8ed1ab_0.conda -sha256: 5577623b9f6685ece2697c6eb7511b4c9ac5fb607c9babc2646c811b428fd46a -md5: 6b6ece66ebcae2d5f326c77ef2c5a066 +size: 1780773 +timestamp: 1778084251775 +- conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.20.0-pyhd8ed1ab_0.conda +sha256: cf70b2f5ad9ae472b71235e5c8a736c9316df3705746de419b59d442e8348e86 +md5: 16c18772b340887160c79a6acc022db0 depends: -- python >=3.9 +- python >=3.10 license: BSD-2-Clause license_family: BSD -size: 889287 -timestamp: 1750615908735 +size: 893031 +timestamp: 1774796815820 - conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda sha256: ba3b032fa52709ce0d9fd388f63d330a026754587a2f461117cac9ab73d8d0d8 md5: 461219d1a5bd61342293efa2c0c90eac @@ -1153,31 +1127,31 @@ license: BSD-3-Clause license_family: BSD size: 21085 timestamp: 1733217331982 -- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/python-3.14.3-hb06a95a_101_cp314.conda -build_number: 101 -sha256: 87e9dff5646aba87cecfbc08789634c855871a7325169299d749040b0923a356 -md5: 205011b36899ff0edf41b3db0eda5a44 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/python-3.14.5-hfd9ac0a_100_cp314.conda +build_number: 100 +sha256: d37bad5447365346166c72950ea8f49689aa49cecc1b0623d00458427627b8df +md5: d956e09feb806f5974675ce92ad81d45 depends: - bzip2 >=1.0.8,<2.0a0 - ld_impl_linux-aarch64 >=2.36.1 -- libexpat >=2.7.3,<3.0a0 +- libexpat >=2.8.0,<3.0a0 - libffi >=3.5.2,<3.6.0a0 - libgcc >=14 -- liblzma >=5.8.2,<6.0a0 +- liblzma >=5.8.3,<6.0a0 - libmpdec >=4.0.0,<5.0a0 -- libsqlite >=3.51.2,<4.0a0 -- libuuid >=2.41.3,<3.0a0 -- libzlib >=1.3.1,<2.0a0 -- ncurses >=6.5,<7.0a0 -- openssl >=3.5.5,<4.0a0 +- libsqlite >=3.53.1,<4.0a0 +- libuuid >=2.42.1,<3.0a0 +- libzlib >=1.3.2,<2.0a0 +- ncurses >=6.6,<7.0a0 +- openssl >=3.5.6,<4.0a0 - python_abi 3.14.* *_cp314 - readline >=8.3,<9.0a0 - tk >=8.6.13,<8.7.0a0 - tzdata - zstd >=1.5.7,<1.6.0a0 license: Python-2.0 -size: 37305578 -timestamp: 1770674395875 +size: 37510439 +timestamp: 1779236267040 python_site_packages_path: lib/python3.14/site-packages - conda: https://conda.anaconda.org/conda-forge/noarch/python-dotenv-1.2.2-pyhcf101f3_0.conda sha256: 74e417a768f59f02a242c25e7db0aa796627b5bc8c818863b57786072aeb85e5 @@ -1188,15 +1162,15 @@ license: BSD-3-Clause license_family: BSD size: 27848 timestamp: 1772388605021 -- conda: https://conda.anaconda.org/conda-forge/noarch/python-gil-3.14.3-h4df99d1_101.conda -sha256: 233aebd94c704ac112afefbb29cf4170b7bc606e22958906f2672081bc50638a -md5: 235765e4ea0d0301c75965985163b5a1 +- conda: https://conda.anaconda.org/conda-forge/noarch/python-gil-3.14.5-h4df99d1_100.conda +sha256: 41dd7da285d71d519257fa7dacb1cae060d5ebfaa5f92cba5994899d2978e943 +md5: 41954747ba952ec4b01e16c2c9e8d8ff depends: -- cpython 3.14.3.* +- cpython 3.14.5.* - python_abi * *_cp314 license: Python-2.0 -size: 50062 -timestamp: 1770674497152 +size: 50212 +timestamp: 1779236703009 - conda: https://conda.anaconda.org/conda-forge/noarch/python-kaleido-0.2.1-pyhd8ed1ab_0.tar.bz2 sha256: e17bf63a30aec33432f1ead86e15e9febde9fc40a7f869c0e766be8d2db44170 md5: 310259a5b03ff02289d7705f39e2b1d2 @@ -1253,9 +1227,9 @@ license: MIT license_family: MIT size: 51788 timestamp: 1760379115194 -- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/regex-2026.2.28-py314h51f160d_0.conda -sha256: 2080ecea825e1ef91a2422cc0bc63e85db9e38908ed17657fb8f41de7a6eee71 -md5: 818aa2c9f6b3c808da5e7be22a9a424c +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/regex-2026.5.9-py314h51f160d_0.conda +sha256: 05ef55f09f31eabd0a205f6b065e13fc746675f41924620977692ef0ffe5aad8 +md5: 34ed7bc9febeca70f55b757ca09c354d depends: - libgcc >=14 - python >=3.14,<3.15.0a0 @@ -1263,27 +1237,27 @@ depends: - python_abi 3.14.* *_cp314 license: Apache-2.0 AND CNRI-Python license_family: PSF -size: 408097 -timestamp: 1772255205521 -- conda: https://conda.anaconda.org/conda-forge/noarch/requests-2.32.5-pyhcf101f3_1.conda -sha256: 7813c38b79ae549504b2c57b3f33394cea4f2ad083f0994d2045c2e24cb538c5 -md5: c65df89a0b2e321045a9e01d1337b182 +size: 409780 +timestamp: 1778374195988 +- conda: https://conda.anaconda.org/conda-forge/noarch/requests-2.34.2-pyhcf101f3_0.conda +sha256: 1715246b19c9f85ee022933b4845f2fc14ac9184981b7b7d9b728bec8e9588da +md5: 4a85203c1d80c1059086ae860836ffb9 depends: - python >=3.10 -- certifi >=2017.4.17 +- certifi >=2023.5.7 - charset-normalizer >=2,<4 - idna >=2.5,<4 -- urllib3 >=1.21.1,<3 +- urllib3 >=1.26,<3 - python constrains: -- chardet >=3.0.2,<6 +- chardet >=3.0.2,<8 license: Apache-2.0 license_family: APACHE -size: 63602 -timestamp: 1766926974520 -- conda: https://conda.anaconda.org/conda-forge/noarch/rich-14.3.3-pyhcf101f3_0.conda -sha256: b06ce84d6a10c266811a7d3adbfa1c11f13393b91cc6f8a5b468277d90be9590 -md5: 7a6289c50631d620652f5045a63eb573 +size: 68709 +timestamp: 1778851103479 +- conda: https://conda.anaconda.org/conda-forge/noarch/rich-15.0.0-pyhcf101f3_0.conda +sha256: 3d6ba2c0fcdac3196ba2f0615b4104e532525ffa1335b50a2878be5ff488814a +md5: 0242025a3c804966bf71aa04eee82f66 depends: - markdown-it-py >=2.2.0 - pygments >=2.13.0,<3.0.0 @@ -1292,8 +1266,8 @@ depends: - python license: MIT license_family: MIT -size: 208472 -timestamp: 1771572730357 +size: 208577 +timestamp: 1775991661559 - conda: https://conda.anaconda.org/conda-forge/noarch/rich-click-1.9.7-pyh8f84b5b_0.conda sha256: aa3fcb167321bae51998de2e94d199109c9024f25a5a063cb1c28d8f1af33436 md5: 0c20a8ebcddb24a45da89d5e917e6cb9 @@ -1331,19 +1305,18 @@ license: MIT license_family: MIT size: 22284 timestamp: 1735770589188 -- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/sqlite-3.52.0-hf1c7be2_0.conda -sha256: 4f8523f5341f0d9e1547085206c6c1f71f9fc7c277443ca363a8cf98add8fc01 -md5: d9634079df93a65ee045b3c75f35cae1 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/sqlite-3.53.1-he8854b5_0.conda +sha256: 27467e4bfb0681546f149718c33b806fec078185fbaa6a4d17d440bc8f56185c +md5: 46009bdca2315a99e0a3a7d0ba1af3b9 depends: -- icu >=78.2,<79.0a0 - libgcc >=14 -- libsqlite 3.52.0 h10b116e_0 -- libzlib >=1.3.1,<2.0a0 -- ncurses >=6.5,<7.0a0 +- libsqlite 3.53.1 h022381a_0 +- libzlib >=1.3.2,<2.0a0 +- ncurses >=6.6,<7.0a0 - readline >=8.3,<9.0a0 license: blessing -size: 209416 -timestamp: 1772818891689 +size: 209964 +timestamp: 1777986493350 - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/tiktoken-0.12.0-py314h6a36e60_3.conda sha256: c1da41c79262b27efa168407cfecc47b20270e5fc071a8307f95a2c85fb94170 md5: 55bf7b559202236157b14323b40f19e6 @@ -1382,20 +1355,20 @@ depends: license: MPL-2.0 and MIT size: 94132 timestamp: 1770153424136 -- conda: https://conda.anaconda.org/conda-forge/noarch/typeguard-4.5.1-pyhd8ed1ab_0.conda -sha256: 39d8ae33c43cdb8f771373e149b0b4fae5a08960ac58dcca95b2f1642bb17448 -md5: 260af1b0a94f719de76b4e14094e9a3b +- conda: https://conda.anaconda.org/conda-forge/noarch/typeguard-4.5.2-pyhcf101f3_0.conda +sha256: 59d7851d32fddb5b510272e6557aa982edeb927d349648dac27f5bf01d18bb26 +md5: 4460f039b7dedf15f7df086446ca75ae depends: -- importlib-metadata >=3.6 -- python >=3.10 -- typing-extensions >=4.10.0 - typing_extensions >=4.14.0 +- python >=3.10 +- importlib-metadata >=3.6 +- python constrains: - pytest >=7 license: MIT license_family: MIT -size: 36838 -timestamp: 1771532971545 +size: 38297 +timestamp: 1778779291237 - conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.15.0-h396c80c_0.conda sha256: 7c2df5721c742c2a47b2c8f960e718c930031663ac1174da67c1ed5999f7938c md5: edd329d7d3a4ab45dcf905899a7a6115 @@ -1405,16 +1378,17 @@ license: PSF-2.0 license_family: PSF size: 91383 timestamp: 1756220668932 -- conda: https://conda.anaconda.org/conda-forge/noarch/typing-inspection-0.4.2-pyhd8ed1ab_1.conda -sha256: 70db27de58a97aeb7ba7448366c9853f91b21137492e0b4430251a1870aa8ff4 -md5: a0a4a3035667fc34f29bfbd5c190baa6 +- conda: https://conda.anaconda.org/conda-forge/noarch/typing-inspection-0.4.2-pyhcf101f3_2.conda +sha256: 8b90d2f19f9458b8c58a55e1fcdc1d90c1603a847a47654d8a454549413ba60a +md5: 53f5409c5cfd6c5a66417d68e3f0a864 depends: - python >=3.10 - typing_extensions >=4.12.0 +- python license: MIT license_family: MIT -size: 18923 -timestamp: 1764158430324 +size: 20935 +timestamp: 1777105465795 - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda sha256: 032271135bca55aeb156cee361c81350c6f3fb203f57d024d7e5a1fc9ef18731 md5: 0caa1af407ecff61170c9437a808404d @@ -1431,9 +1405,9 @@ md5: ad659d0a2b3e47e38d829aa8cad2d610 license: LicenseRef-Public-Domain size: 119135 timestamp: 1767016325805 -- conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.6.3-pyhd8ed1ab_0.conda -sha256: af641ca7ab0c64525a96fd9ad3081b0f5bcf5d1cbb091afb3f6ed5a9eee6111a -md5: 9272daa869e03efe68833e3dc7a02130 +- conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.7.0-pyhd8ed1ab_0.conda +sha256: feff959a816f7988a0893201aa9727bbb7ee1e9cec2c4f0428269b489eb93fb4 +md5: cbb88288f74dbe6ada1c6c7d0a97223e depends: - backports.zstd >=1.0.0 - brotli-python >=1.2.0 @@ -1442,8 +1416,8 @@ depends: - python >=3.10 license: MIT license_family: MIT -size: 103172 -timestamp: 1767817860341 +size: 103560 +timestamp: 1778188657149 - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/xorg-libxau-1.0.12-he30d5cf_1.conda sha256: e9f6e931feeb2f40e1fdbafe41d3b665f1ab6cb39c5880a1fcf9f79a3f3c84a5 md5: 1c246e1105000c3660558459e2fd6d43 @@ -1471,16 +1445,16 @@ license: MIT license_family: MIT size: 88088 timestamp: 1753484092643 -- conda: https://conda.anaconda.org/conda-forge/noarch/zipp-3.23.0-pyhcf101f3_1.conda -sha256: b4533f7d9efc976511a73ef7d4a2473406d7f4c750884be8e8620b0ce70f4dae -md5: 30cd29cb87d819caead4d55184c1d115 +- conda: https://conda.anaconda.org/conda-forge/noarch/zipp-4.1.0-pyhcf101f3_0.conda +sha256: 210bd31c22bb88f5e2a167df24c95bb5f152b2ada7502f9b8c49d1f5366db423 +md5: ba3dcdc8584155c97c648ae9c044b7a3 depends: - python >=3.10 - python license: MIT license_family: MIT -size: 24194 -timestamp: 1764460141901 +size: 24190 +timestamp: 1779159948016 - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/zlib-ng-2.3.3-ha7cb516_1.conda sha256: 638a3a41a4fbfed52d3c60c8ef5a3693b3f12a5b1a3f58fa29f5698d0a0702e2 md5: f731af71c723065d91b4c01bb822641b diff --git a/modules/nf-core/multiqc/.conda-lock/linux_arm64-bd-d167b8012595a136_1.txt b/modules/nf-core/multiqc/.conda-lock/linux_arm64-bd-d167b8012595a136_1.txt deleted file mode 100644 index f787dbe..0000000 --- a/modules/nf-core/multiqc/.conda-lock/linux_arm64-bd-d167b8012595a136_1.txt +++ /dev/null @@ -1,125 +0,0 @@ - -# This file may be used to create an environment using: -# $ conda create --name --file -# platform: linux-aarch64 -@EXPLICIT -https://conda.anaconda.org/conda-forge/linux-aarch64/libgomp-15.2.0-h8acb6b2_18.conda#4faa39bf919939602e594253bd673958 -https://conda.anaconda.org/conda-forge/linux-aarch64/_openmp_mutex-4.5-20_gnu.conda#468fd3bb9e1f671d36c2cbc677e56f1d -https://conda.anaconda.org/conda-forge/linux-aarch64/libgcc-15.2.0-h8acb6b2_18.conda#552567ea2b61e3a3035759b2fdb3f9a6 -https://conda.anaconda.org/conda-forge/linux-aarch64/bzip2-1.0.8-h4777abc_9.conda#840d8fc0d7b3209be93080bc20e07f2d -https://conda.anaconda.org/conda-forge/linux-aarch64/libzlib-1.3.2-hdc9db2a_2.conda#502006882cf5461adced436e410046d1 -https://conda.anaconda.org/conda-forge/linux-aarch64/zstd-1.5.7-h85ac4a6_6.conda#c3655f82dcea2aa179b291e7099c1fcc -https://conda.anaconda.org/conda-forge/linux-aarch64/ld_impl_linux-aarch64-2.45.1-default_h1979696_102.conda#a21644fc4a83da26452a718dc9468d5f -https://conda.anaconda.org/conda-forge/linux-aarch64/libexpat-2.7.5-hfae3067_0.conda#05d1e0b30acd816a192c03dc6e164f4d -https://conda.anaconda.org/conda-forge/linux-aarch64/libffi-3.5.2-h376a255_0.conda#2f364feefb6a7c00423e80dcb12db62a -https://conda.anaconda.org/conda-forge/linux-aarch64/liblzma-5.8.3-he30d5cf_0.conda#76298a9e6d71ee6e832a8d0d7373b261 -https://conda.anaconda.org/conda-forge/linux-aarch64/libmpdec-4.0.0-he30d5cf_1.conda#7b9813e885482e3ccb1fa212b86d7fd0 -https://conda.anaconda.org/conda-forge/linux-aarch64/libsqlite-3.53.0-h022381a_0.conda#86db4036fd08bf34e991bf48a8af405d -https://conda.anaconda.org/conda-forge/linux-aarch64/libuuid-2.42-h1022ec0_0.conda#a0b5de740d01c390bdbb46d7503c9fab -https://conda.anaconda.org/conda-forge/linux-aarch64/ncurses-6.5-ha32ae93_3.conda#182afabe009dc78d8b73100255ee6868 -https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.4.22-hbd8a1cb_0.conda#e18ad67cf881dcadee8b8d9e2f8e5f73 -https://conda.anaconda.org/conda-forge/linux-aarch64/openssl-3.6.2-h546c87b_0.conda#3b129669089e4d6a5c6871dbb4669b99 -https://conda.anaconda.org/conda-forge/noarch/python_abi-3.14-8_cp314.conda#0539938c55b6b1a59b560e843ad864a4 -https://conda.anaconda.org/conda-forge/linux-aarch64/readline-8.3-hb682ff5_0.conda#3d49cad61f829f4f0e0611547a9cda12 -https://conda.anaconda.org/conda-forge/linux-aarch64/tk-8.6.13-noxft_h0dc03b3_103.conda#7fc6affb9b01e567d2ef1d05b84aa6ed -https://conda.anaconda.org/conda-forge/noarch/tzdata-2025c-hc9c84f9_1.conda#ad659d0a2b3e47e38d829aa8cad2d610 -https://conda.anaconda.org/conda-forge/linux-aarch64/python-3.14.4-hfd9ac0a_100_cp314.conda#3cfbe780f0f51cc8cba41db9f8a28bfe -https://conda.anaconda.org/conda-forge/noarch/cpython-3.14.4-py314hd8ed1ab_100.conda#f111d4cfaf1fe9496f386bc98ae94452 -https://conda.anaconda.org/conda-forge/noarch/python-gil-3.14.4-h4df99d1_100.conda#e4e60721757979d01d3964122f674959 -https://conda.anaconda.org/conda-forge/noarch/_python_abi3_support-1.0-hd8ed1ab_2.conda#aaa2a381ccc56eac91d63b6c1240312f -https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda#0caa1af407ecff61170c9437a808404d -https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.15.0-h396c80c_0.conda#edd329d7d3a4ab45dcf905899a7a6115 -https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.7.0-pyhd8ed1ab_1.conda#2934f256a8acfe48f6ebb4fce6cde29c -https://conda.anaconda.org/conda-forge/noarch/attrs-26.1.0-pyhcf101f3_0.conda#c6b0543676ecb1fb2d7643941fe375f2 -https://conda.anaconda.org/conda-forge/noarch/backports.zstd-1.3.0-py314h680f03e_0.conda#a2ac7763a9ac75055b68f325d3255265 -https://conda.anaconda.org/conda-forge/linux-aarch64/libstdcxx-15.2.0-hef695bb_18.conda#f56573d05e3b735cb03efeb64a15f388 -https://conda.anaconda.org/conda-forge/linux-aarch64/brotli-python-1.2.0-py314h352cb57_1.conda#a1b5c571a0923a205d663d8678df4792 -https://conda.anaconda.org/conda-forge/noarch/certifi-2026.4.22-pyhd8ed1ab_0.conda#929471569c93acefb30282a22060dcd5 -https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.7-pyhd8ed1ab_0.conda#a9167b9571f3baa9d448faa2139d1089 -https://conda.anaconda.org/conda-forge/noarch/click-8.3.2-pyhc90fa1f_0.conda#4d18bc3af7cfcea97bd817164672a08c -https://conda.anaconda.org/conda-forge/noarch/humanfriendly-10.0-pyh707e725_8.conda#7fe569c10905402ed47024fc481bb371 -https://conda.anaconda.org/conda-forge/noarch/coloredlogs-15.0.1-pyhd8ed1ab_4.conda#b866ff7007b934d564961066c8195983 -https://conda.anaconda.org/conda-forge/noarch/networkx-3.6.1-pyhcf101f3_0.conda#a2c1eeadae7a309daed9d62c96012a2b -https://conda.anaconda.org/conda-forge/linux-aarch64/libgfortran5-15.2.0-h1b7bec0_18.conda#574d88ce3348331e962cfa5ed451b247 -https://conda.anaconda.org/conda-forge/linux-aarch64/libgfortran-15.2.0-he9431aa_18.conda#41f261f5e4e2e8cbd236c2f1f15dae1b -https://conda.anaconda.org/conda-forge/linux-aarch64/libopenblas-0.3.32-pthreads_h9d3fd7e_0.conda#5d2ce5cf40443d055ec6d33840192265 -https://conda.anaconda.org/conda-forge/linux-aarch64/libblas-3.11.0-6_haddc8a3_openblas.conda#652bb20bb4618cacd11e17ae070f47ce -https://conda.anaconda.org/conda-forge/linux-aarch64/libcblas-3.11.0-6_hd72aa62_openblas.conda#939e300b110db241a96a1bed438c315b -https://conda.anaconda.org/conda-forge/linux-aarch64/liblapack-3.11.0-6_h88aeb00_openblas.conda#e23a27b52fb320687239e2c5ae4d7540 -https://conda.anaconda.org/conda-forge/linux-aarch64/numpy-2.4.3-py314haac167e_0.conda#25d896c331481145720a21e5145fad65 -https://conda.anaconda.org/conda-forge/noarch/colormath-3.0.0-pyhd8ed1ab_4.conda#071cf7b0ce333c81718b054066c15102 -https://conda.anaconda.org/conda-forge/linux-aarch64/expat-2.7.5-hfae3067_0.conda#d2bb0c889d94f2fdc5856392c3002976 -https://conda.anaconda.org/conda-forge/noarch/font-ttf-dejavu-sans-mono-2.37-hab24e00_0.tar.bz2#0c96522c6bdaed4b1566d11387caaf45 -https://conda.anaconda.org/conda-forge/noarch/font-ttf-inconsolata-3.000-h77eed37_0.tar.bz2#34893075a5c9e55cdafac56607368fc6 -https://conda.anaconda.org/conda-forge/noarch/font-ttf-source-code-pro-2.038-h77eed37_0.tar.bz2#4d59c254e01d9cde7957100457e2d5fb -https://conda.anaconda.org/conda-forge/noarch/font-ttf-ubuntu-0.83-h77eed37_3.conda#49023d73832ef61042f6a237cb2687e7 -https://conda.anaconda.org/conda-forge/linux-aarch64/libpng-1.6.58-h1abf092_0.conda#f51503ac45a4888bce71af9027a2ecc9 -https://conda.anaconda.org/conda-forge/linux-aarch64/libfreetype6-2.14.3-hdae7a39_0.conda#b99ed99e42dafb27889483b3098cace7 -https://conda.anaconda.org/conda-forge/linux-aarch64/libfreetype-2.14.3-h8af1aa0_0.conda#a229e22d4d8814a07702b0919d8e6701 -https://conda.anaconda.org/conda-forge/linux-aarch64/fontconfig-2.17.1-hba86a56_0.conda#0fed1ff55f4938a65907f3ecf62609db -https://conda.anaconda.org/conda-forge/noarch/fonts-conda-forge-1-hc364b38_1.conda#a7970cd949a077b7cb9696379d338681 -https://conda.anaconda.org/conda-forge/noarch/hpack-4.1.0-pyhd8ed1ab_0.conda#0a802cb9888dd14eeefc611f05c40b6e -https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.1.0-pyhd8ed1ab_0.conda#8e6923fc12f1fe8f8c4e5c9f343256ac -https://conda.anaconda.org/conda-forge/noarch/h2-4.3.0-pyhcf101f3_0.conda#164fc43f0b53b6e3a7bc7dce5e4f1dc9 -https://conda.anaconda.org/conda-forge/noarch/humanize-4.15.0-pyhd8ed1ab_0.conda#daddf757c3ecd6067b9af1df1f25d89e -https://conda.anaconda.org/conda-forge/noarch/idna-3.13-pyhcf101f3_0.conda#fb7130c190f9b4ec91219840a05ba3ac -https://conda.anaconda.org/conda-forge/noarch/zipp-3.23.1-pyhcf101f3_0.conda#e1c36c6121a7c9c76f2f148f1e83b983 -https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.8.0-pyhcf101f3_0.conda#080594bf4493e6bae2607e65390c520a -https://conda.anaconda.org/conda-forge/linux-aarch64/markupsafe-3.0.3-py314hb76de3f_1.conda#e5de3c36dd548b35ff2a8aa49208dcb3 -https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.6-pyhcf101f3_1.conda#04558c96691bed63104678757beb4f8d -https://conda.anaconda.org/conda-forge/linux-aarch64/rpds-py-0.30.0-py314h02b7a91_0.conda#e7f6ed9e60043bb5cbcc527764897f0d -https://conda.anaconda.org/conda-forge/noarch/referencing-0.37.0-pyhcf101f3_0.conda#870293df500ca7e18bedefa5838a22ab -https://conda.anaconda.org/conda-forge/noarch/jsonschema-specifications-2025.9.1-pyhcf101f3_0.conda#439cd0f567d697b20a8f45cb70a1005a -https://conda.anaconda.org/conda-forge/noarch/jsonschema-4.26.0-pyhcf101f3_0.conda#ada41c863af263cc4c5fcbaff7c3e4dc -https://conda.anaconda.org/conda-forge/linux-aarch64/libgcc-ng-15.2.0-he9431aa_18.conda#4feebd0fbf61075a1a9c2e9b3936c257 -https://conda.anaconda.org/conda-forge/linux-aarch64/mathjax-2.7.7-h8af1aa0_3.tar.bz2#7b08314a6867a9d5648a1c3265e9eb8e -https://conda.anaconda.org/conda-forge/linux-aarch64/nspr-4.38-h3ad9384_0.conda#6dd4f07147774bf720075a210f8026b9 -https://conda.anaconda.org/conda-forge/linux-aarch64/nss-3.118-h544fa81_0.conda#4540f9570d12db2150f42ba036154552 -https://conda.anaconda.org/conda-forge/linux-aarch64/sqlite-3.53.0-he8854b5_0.conda#ad8164bdeece883b825c50639c0c4725 -https://conda.anaconda.org/conda-forge/linux-aarch64/kaleido-core-0.2.1-he5a581e_0.tar.bz2#4f0d284f5d11e04277b552eb1c172c7f -https://conda.anaconda.org/conda-forge/linux-aarch64/libjpeg-turbo-3.1.4.1-he30d5cf_0.conda#a85ba48648f6868016f2741fd9170250 -https://conda.anaconda.org/conda-forge/linux-aarch64/lerc-4.1.0-h52b7260_0.conda#d13423b06447113a90b5b1366d4da171 -https://conda.anaconda.org/conda-forge/linux-aarch64/libdeflate-1.25-h1af38f5_0.conda#a9138815598fe6b91a1d6782ca657b0c -https://conda.anaconda.org/conda-forge/linux-aarch64/libwebp-base-1.6.0-ha2e29f5_0.conda#24e92d0942c799db387f5c9d7b81f1af -https://conda.anaconda.org/conda-forge/linux-aarch64/libtiff-4.7.1-hdb009f0_1.conda#8c6fd84f9c87ac00636007c6131e457d -https://conda.anaconda.org/conda-forge/linux-aarch64/lcms2-2.18-h9d5b58d_0.conda#bb960f01525b5e001608afef9d47b79c -https://conda.anaconda.org/conda-forge/linux-aarch64/pthread-stubs-0.4-h86ecc28_1002.conda#bb5a90c93e3bac3d5690acf76b4a6386 -https://conda.anaconda.org/conda-forge/linux-aarch64/xorg-libxau-1.0.12-he30d5cf_1.conda#1c246e1105000c3660558459e2fd6d43 -https://conda.anaconda.org/conda-forge/linux-aarch64/xorg-libxdmcp-1.1.5-he30d5cf_1.conda#bff06dcde4a707339d66d45d96ceb2e2 -https://conda.anaconda.org/conda-forge/linux-aarch64/libxcb-1.17.0-h262b8f6_0.conda#cd14ee5cca2464a425b1dbfc24d90db2 -https://conda.anaconda.org/conda-forge/noarch/markdown-3.10.2-pyhcf101f3_0.conda#ba0a9221ce1063f31692c07370d062f3 -https://conda.anaconda.org/conda-forge/noarch/mdurl-0.1.2-pyhd8ed1ab_1.conda#592132998493b3ff25fd7479396e8351 -https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-4.0.0-pyhd8ed1ab_0.conda#5b5203189eb668f042ac2b0826244964 -https://conda.anaconda.org/conda-forge/noarch/natsort-8.4.0-pyhcf101f3_2.conda#e941e85e273121222580723010bd4fa2 -https://conda.anaconda.org/conda-forge/noarch/packaging-26.1-pyhc364b38_0.conda#b8ae38639d323d808da535fb71e31be8 -https://conda.anaconda.org/conda-forge/linux-aarch64/openjpeg-2.5.4-h5da879a_0.conda#cea962410e327262346d48d01f05936c -https://conda.anaconda.org/conda-forge/linux-aarch64/zlib-ng-2.3.3-ha7cb516_1.conda#f731af71c723065d91b4c01bb822641b -https://conda.anaconda.org/conda-forge/linux-aarch64/pillow-12.2.0-py314hac3e5ec_0.conda#87d58d103b47c4a8567b3d7666647684 -https://conda.anaconda.org/conda-forge/noarch/narwhals-2.20.0-pyhcf101f3_0.conda#6cac1a50359219d786453c6fef819f98 -https://conda.anaconda.org/conda-forge/noarch/plotly-6.6.0-pyhd8ed1ab_0.conda#3e9427ee186846052e81fadde8ebe96a -https://conda.anaconda.org/conda-forge/linux-aarch64/polars-runtime-32-1.40.0-py310hff09b76_0.conda#d5628a33ce7652511e38fc98643dc910 -https://conda.anaconda.org/conda-forge/noarch/polars-1.40.0-pyh58ad624_0.conda#fd16be490f5403adfbf27dd4901bbe34 -https://conda.anaconda.org/conda-forge/linux-aarch64/polars-runtime-compat-1.40.0-py310hf00a4a2_0.conda#a82af0fcbb72db253dc89a7a45279372 -https://conda.anaconda.org/conda-forge/noarch/polars-lts-cpu-1.34.0.deprecated-hc364b38_0.conda#ef0340e75068ac8ff96462749b5c98e7 -https://conda.anaconda.org/conda-forge/linux-aarch64/yaml-0.2.5-h80f16a2_3.conda#032d8030e4a24fe1f72c74423a46fb88 -https://conda.anaconda.org/conda-forge/linux-aarch64/pyyaml-6.0.3-py314h807365f_1.conda#9ae2c92975118058bd720e9ba2bb7c58 -https://conda.anaconda.org/conda-forge/noarch/pyaml-env-1.2.2-pyhd8ed1ab_0.conda#e17be1016bcc3516827b836cd3e4d9dc -https://conda.anaconda.org/conda-forge/linux-aarch64/pydantic-core-2.46.3-py314h451b6cc_0.conda#1a2cb55be9a153ad6203bff6b787c240 -https://conda.anaconda.org/conda-forge/noarch/typing-inspection-0.4.2-pyhd8ed1ab_1.conda#a0a4a3035667fc34f29bfbd5c190baa6 -https://conda.anaconda.org/conda-forge/noarch/pydantic-2.13.3-pyhcf101f3_0.conda#f690e6f204efd2e5c06b57518a383d98 -https://conda.anaconda.org/conda-forge/noarch/python-dotenv-1.2.2-pyhcf101f3_0.conda#130584ad9f3a513cdd71b1fdc1244e9c -https://conda.anaconda.org/conda-forge/noarch/python-kaleido-0.2.1-pyhd8ed1ab_0.tar.bz2#310259a5b03ff02289d7705f39e2b1d2 -https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda#461219d1a5bd61342293efa2c0c90eac -https://conda.anaconda.org/conda-forge/noarch/urllib3-2.6.3-pyhd8ed1ab_0.conda#9272daa869e03efe68833e3dc7a02130 -https://conda.anaconda.org/conda-forge/noarch/requests-2.33.1-pyhcf101f3_0.conda#10afbb4dbf06ff959ad25a92ccee6e59 -https://conda.anaconda.org/conda-forge/noarch/pygments-2.20.0-pyhd8ed1ab_0.conda#16c18772b340887160c79a6acc022db0 -https://conda.anaconda.org/conda-forge/noarch/rich-15.0.0-pyhcf101f3_0.conda#0242025a3c804966bf71aa04eee82f66 -https://conda.anaconda.org/conda-forge/noarch/rich-click-1.9.7-pyh8f84b5b_0.conda#0c20a8ebcddb24a45da89d5e917e6cb9 -https://conda.anaconda.org/conda-forge/noarch/spectra-0.0.11-pyhd8ed1ab_2.conda#472239e4eb7b5a84bb96b3ed7e3a596a -https://conda.anaconda.org/conda-forge/linux-aarch64/regex-2026.4.4-py314h51f160d_0.conda#88a3dbd279e6b1faf0cddb8397866864 -https://conda.anaconda.org/conda-forge/linux-aarch64/tiktoken-0.12.0-py314h6a36e60_3.conda#55bf7b559202236157b14323b40f19e6 -https://conda.anaconda.org/conda-forge/noarch/tqdm-4.67.3-pyh8f84b5b_0.conda#e5ce43272193b38c2e9037446c1d9206 -https://conda.anaconda.org/conda-forge/noarch/typeguard-4.5.1-pyhd8ed1ab_0.conda#260af1b0a94f719de76b4e14094e9a3b -https://conda.anaconda.org/bioconda/noarch/multiqc-1.34-pyhdfd78af_0.conda#a7111ab9a6a6146b40cbce16655ac873 -https://conda.anaconda.org/conda-forge/noarch/pip-26.0.1-pyh145f28c_0.conda#09a970fbf75e8ed1aa633827ded6aa4f -https://conda.anaconda.org/conda-forge/linux-aarch64/procps-ng-4.0.6-h1779866_0.conda#ab7288cc39545556d1bc5e71ab2df9a9 diff --git a/modules/nf-core/multiqc/environment.yml b/modules/nf-core/multiqc/environment.yml index 37e7612..7a970e2 100644 --- a/modules/nf-core/multiqc/environment.yml +++ b/modules/nf-core/multiqc/environment.yml @@ -4,4 +4,4 @@ channels: - conda-forge - bioconda dependencies: - - bioconda::multiqc=1.34 + - bioconda::multiqc=1.35 diff --git a/modules/nf-core/multiqc/main.nf b/modules/nf-core/multiqc/main.nf index e80e8cd..c4bc715 100644 --- a/modules/nf-core/multiqc/main.nf +++ b/modules/nf-core/multiqc/main.nf @@ -4,8 +4,8 @@ process MULTIQC { conda "${moduleDir}/environment.yml" container "${workflow.containerEngine in ['singularity', 'apptainer'] && !task.ext.singularity_pull_docker_container - ? 'https://community-cr-prod.seqera.io/docker/registry/v2/blobs/sha256/1b/1bef8af6be88c5733461959c46ac8ef73d18f65277f62a1695d0e1633054f9c2/data' - : 'community.wave.seqera.io/library/multiqc:1.34--db7c73dae76bc9e6'}" + ? 'https://community-cr-prod.seqera.io/docker/registry/v2/blobs/sha256/c8/c8e346f4f6080eadf1253505e6ff09ef004454fc18e8d672006fd7b222cc412e/data' + : 'community.wave.seqera.io/library/multiqc:1.35--c17fb751507e9dfc'}" input: tuple val(meta), path(multiqc_files, stageAs: "?/*"), path(multiqc_config, stageAs: "?/*"), path(multiqc_logo), path(replace_names), path(sample_names) diff --git a/modules/nf-core/multiqc/meta.yml b/modules/nf-core/multiqc/meta.yml index 2facc62..27ce18d 100644 --- a/modules/nf-core/multiqc/meta.yml +++ b/modules/nf-core/multiqc/meta.yml @@ -110,24 +110,24 @@ maintainers: containers: conda: linux/amd64: - lock_file: modules/nf-core/multiqc/.conda-lock/linux_amd64-bd-db7c73dae76bc9e6_1.txt + lock_file: modules/nf-core/multiqc/.conda-lock/linux_amd64-bd-c17fb751507e9dfc_1.txt linux/arm64: - lock_file: modules/nf-core/multiqc/.conda-lock/linux_arm64-bd-d167b8012595a136_1.txt + lock_file: modules/nf-core/multiqc/.conda-lock/linux_arm64-bd-5c84a5000a226ab5_1.txt docker: linux/amd64: - name: community.wave.seqera.io/library/multiqc:1.34--db7c73dae76bc9e6 - build_id: bd-db7c73dae76bc9e6_1 - scan_id: sc-66fc7138dbf1cf48_1 + name: community.wave.seqera.io/library/multiqc:1.35--c17fb751507e9dfc + build_id: bd-c17fb751507e9dfc_1 + scan_id: sc-3b1b3932f9846892_1 linux/arm64: - name: community.wave.seqera.io/library/multiqc:1.34--d167b8012595a136 - build_id: bd-d167b8012595a136_1 - scan_id: sc-ac701dfa631a2af9_1 + name: community.wave.seqera.io/library/multiqc:1.35--5c84a5000a226ab5 + build_id: bd-5c84a5000a226ab5_1 + scan_id: sc-0d39df41e9737bbd_1 singularity: linux/amd64: - name: oras://community.wave.seqera.io/library/multiqc:1.34--4fc8657c816047c0 - build_id: bd-4fc8657c816047c0_1 - https: https://community-cr-prod.seqera.io/docker/registry/v2/blobs/sha256/1b/1bef8af6be88c5733461959c46ac8ef73d18f65277f62a1695d0e1633054f9c2/data + name: oras://community.wave.seqera.io/library/multiqc:1.35--c680f2aea25ccec2 + build_id: bd-c680f2aea25ccec2_1 + https: https://community-cr-prod.seqera.io/docker/registry/v2/blobs/sha256/c8/c8e346f4f6080eadf1253505e6ff09ef004454fc18e8d672006fd7b222cc412e/data linux/arm64: - name: oras://community.wave.seqera.io/library/multiqc:1.34--7fbd82d945c06726 - build_id: bd-7fbd82d945c06726_1 - https: https://community-cr-prod.seqera.io/docker/registry/v2/blobs/sha256/9a/9a1fec9662a152683e6fcae440d0ce20920b3b89dc62d1e3a52e73f92eba0969/data + name: oras://community.wave.seqera.io/library/multiqc:1.35--c0468833d65b2f81 + build_id: bd-c0468833d65b2f81_1 + https: https://community-cr-prod.seqera.io/docker/registry/v2/blobs/sha256/e4/e48aa28aebc881254a499b24c3e1ce77b8df1b85a5432699ed6f72eb17ac7fb5/data diff --git a/modules/nf-core/multiqc/tests/main.nf.test.snap b/modules/nf-core/multiqc/tests/main.nf.test.snap index 7c2f370..4489921 100644 --- a/modules/nf-core/multiqc/tests/main.nf.test.snap +++ b/modules/nf-core/multiqc/tests/main.nf.test.snap @@ -81,7 +81,7 @@ [ "MULTIQC", "multiqc", - "1.34" + "1.35" ] ] } @@ -175,7 +175,7 @@ [ "MULTIQC", "multiqc", - "1.34" + "1.35" ] ] } @@ -221,7 +221,7 @@ [ "MULTIQC", "multiqc", - "1.34" + "1.35" ] ] } @@ -314,7 +314,7 @@ [ "MULTIQC", "multiqc", - "1.34" + "1.35" ] ] } @@ -408,7 +408,7 @@ [ "MULTIQC", "multiqc", - "1.34" + "1.35" ] ] } diff --git a/modules/nf-core/samtools/index/environment.yml b/modules/nf-core/samtools/index/environment.yml new file mode 100644 index 0000000..946bb36 --- /dev/null +++ b/modules/nf-core/samtools/index/environment.yml @@ -0,0 +1,10 @@ +--- +# yaml-language-server: $schema=https://raw.githubusercontent.com/nf-core/modules/master/modules/environment-schema.json +channels: + - conda-forge + - bioconda +dependencies: + # renovate: datasource=conda depName=bioconda/htslib + - bioconda::htslib=1.23.1 + # renovate: datasource=conda depName=bioconda/samtools + - bioconda::samtools=1.23.1 diff --git a/modules/nf-core/samtools/index/main.nf b/modules/nf-core/samtools/index/main.nf new file mode 100644 index 0000000..5b5756b --- /dev/null +++ b/modules/nf-core/samtools/index/main.nf @@ -0,0 +1,38 @@ +process SAMTOOLS_INDEX { + tag "${meta.id}" + label 'process_low' + + conda "${moduleDir}/environment.yml" + container "${workflow.containerEngine in ['singularity', 'apptainer'] && !task.ext.singularity_pull_docker_container + ? 'https://community-cr-prod.seqera.io/docker/registry/v2/blobs/sha256/8c/8c5d2818c8b9f58e1fba77ce219fdaf32087ae53e857c4a496402978af26e78c/data' + : 'community.wave.seqera.io/library/htslib_samtools:1.23.1--5b6bb4ede7e612e5'}" + + input: + tuple val(meta), path(input) + + output: + tuple val(meta), path("*.{bai,csi,crai}"), emit: index + tuple val("${task.process}"), val('samtools'), eval("samtools version | sed '1!d;s/.* //'"), emit: versions_samtools, topic: versions + + when: + task.ext.when == null || task.ext.when + + script: + def args = task.ext.args ?: '' + """ + samtools \\ + index \\ + -@ ${task.cpus} \\ + ${args} \\ + ${input} + """ + + stub: + def args = task.ext.args ?: '' + def extension = file(input).getExtension() == 'cram' + ? "crai" + : args.contains("-c") ? "csi" : "bai" + """ + touch ${input}.${extension} + """ +} diff --git a/modules/nf-core/samtools/index/meta.yml b/modules/nf-core/samtools/index/meta.yml new file mode 100644 index 0000000..d4938bc --- /dev/null +++ b/modules/nf-core/samtools/index/meta.yml @@ -0,0 +1,70 @@ +name: samtools_index +description: Index SAM/BAM/CRAM file +keywords: + - index + - bam + - sam + - cram +tools: + - samtools: + description: | + SAMtools is a set of utilities for interacting with and post-processing + short DNA sequence read alignments in the SAM, BAM and CRAM formats, written by Heng Li. + These files are generated as output by short read aligners like BWA. + homepage: http://www.htslib.org/ + documentation: http://www.htslib.org/doc/samtools.html + doi: 10.1093/bioinformatics/btp352 + licence: + - "MIT" + identifier: biotools:samtools +input: + - - meta: + type: map + description: | + Groovy Map containing sample information + e.g. [ id:'test', single_end:false ] + - input: + type: file + description: input file + ontologies: [] +output: + index: + - - meta: + type: map + description: | + Groovy Map containing sample information + e.g. [ id:'test', single_end:false ] + - "*.{bai,csi,crai}": + type: file + description: BAM/CRAM/SAM index file + pattern: "*.{bai,csi,crai}" + ontologies: [] + versions_samtools: + - - ${task.process}: + type: string + description: The name of the process + - samtools: + type: string + description: The name of the tool + - samtools version | sed '1!d;s/.* //': + type: eval + description: The expression to obtain the version of the tool +topics: + versions: + - - ${task.process}: + type: string + description: The name of the process + - samtools: + type: string + description: The name of the tool + - samtools version | sed '1!d;s/.* //': + type: eval + description: The expression to obtain the version of the tool +authors: + - "@drpatelh" + - "@ewels" + - "@maxulysse" +maintainers: + - "@ewels" + - "@maxulysse" + - "@matthdsm" diff --git a/modules/nf-core/samtools/index/tests/csi.nextflow.config b/modules/nf-core/samtools/index/tests/csi.nextflow.config new file mode 100644 index 0000000..4af6d82 --- /dev/null +++ b/modules/nf-core/samtools/index/tests/csi.nextflow.config @@ -0,0 +1,6 @@ +process { + + withName: SAMTOOLS_INDEX { + ext.args = '-c' + } +} diff --git a/modules/nf-core/samtools/index/tests/main.nf.test b/modules/nf-core/samtools/index/tests/main.nf.test new file mode 100644 index 0000000..f9d3922 --- /dev/null +++ b/modules/nf-core/samtools/index/tests/main.nf.test @@ -0,0 +1,155 @@ +nextflow_process { + + name "Test Process SAMTOOLS_INDEX" + script "../main.nf" + process "SAMTOOLS_INDEX" + tag "modules" + tag "modules_nfcore" + tag "samtools" + tag "samtools/index" + + test("bai") { + when { + process { + """ + input[0] = Channel.of([ + [ id:'test', single_end:false ], // meta map + file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/bam/test.paired_end.sorted.bam', checkIfExists: true) + ]) + """ + } + } + + then { + assertAll ( + { assert process.success }, + { assert snapshot( + process.out.index, + process.out.findAll { key, val -> key.startsWith('versions') } + ).match() } + ) + } + } + + test("crai") { + when { + process { + """ + input[0] = Channel.of([ + [ id:'test', single_end:false ], // meta map + file(params.modules_testdata_base_path + 'genomics/homo_sapiens/illumina/cram/test.paired_end.recalibrated.sorted.cram', checkIfExists: true) + ]) + """ + } + } + + then { + assertAll ( + { assert process.success }, + { assert snapshot( + process.out.index, + process.out.findAll { key, val -> key.startsWith('versions') } + ).match() } + ) + } + } + + test("csi") { + config "./csi.nextflow.config" + + when { + process { + """ + input[0] = Channel.of([ + [ id:'test', single_end:false ], // meta map + file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/bam/test.paired_end.sorted.bam', checkIfExists: true) + ]) + """ + } + } + + then { + assertAll ( + { assert process.success }, + { assert snapshot( + file(process.out.index[0][1]).name, + process.out.findAll { key, val -> key.startsWith('versions') } + ).match() } + ) + } + } + + test("bai - stub") { + options "-stub" + when { + process { + """ + input[0] = Channel.of([ + [ id:'test', single_end:false ], // meta map + file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/bam/test.paired_end.sorted.bam', checkIfExists: true) + ]) + """ + } + } + + then { + assertAll ( + { assert process.success }, + { assert snapshot( + process.out.index, + process.out.findAll { key, val -> key.startsWith('versions') } + ).match() } + ) + } + } + + test("crai - stub") { + options "-stub" + when { + process { + """ + input[0] = Channel.of([ + [ id:'test', single_end:false ], // meta map + file(params.modules_testdata_base_path + 'genomics/homo_sapiens/illumina/cram/test.paired_end.recalibrated.sorted.cram', checkIfExists: true) + ]) + """ + } + } + + then { + assertAll ( + { assert process.success }, + { assert snapshot( + process.out.index, + process.out.findAll { key, val -> key.startsWith('versions') } + ).match() } + ) + } + } + + test("csi - stub") { + options "-stub" + config "./csi.nextflow.config" + + when { + process { + """ + input[0] = Channel.of([ + [ id:'test', single_end:false ], // meta map + file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/bam/test.paired_end.sorted.bam', checkIfExists: true) + ]) + """ + } + } + + then { + assertAll ( + { assert process.success }, + { assert snapshot( + process.out.index, + process.out.findAll { key, val -> key.startsWith('versions') } + ).match() } + ) + } + } +} diff --git a/modules/nf-core/samtools/index/tests/main.nf.test.snap b/modules/nf-core/samtools/index/tests/main.nf.test.snap new file mode 100644 index 0000000..337dec7 --- /dev/null +++ b/modules/nf-core/samtools/index/tests/main.nf.test.snap @@ -0,0 +1,156 @@ +{ + "csi - stub": { + "content": [ + [ + [ + { + "id": "test", + "single_end": false + }, + "test.paired_end.sorted.bam.csi:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + { + "versions_samtools": [ + [ + "SAMTOOLS_INDEX", + "samtools", + "1.23.1" + ] + ] + } + ], + "meta": { + "nf-test": "0.9.3", + "nextflow": "25.10.4" + }, + "timestamp": "2026-03-19T09:00:39.171613" + }, + "crai - stub": { + "content": [ + [ + [ + { + "id": "test", + "single_end": false + }, + "test.paired_end.recalibrated.sorted.cram.crai:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + { + "versions_samtools": [ + [ + "SAMTOOLS_INDEX", + "samtools", + "1.23.1" + ] + ] + } + ], + "meta": { + "nf-test": "0.9.3", + "nextflow": "25.10.4" + }, + "timestamp": "2026-03-19T09:00:32.838795" + }, + "bai - stub": { + "content": [ + [ + [ + { + "id": "test", + "single_end": false + }, + "test.paired_end.sorted.bam.bai:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + { + "versions_samtools": [ + [ + "SAMTOOLS_INDEX", + "samtools", + "1.23.1" + ] + ] + } + ], + "meta": { + "nf-test": "0.9.3", + "nextflow": "25.10.4" + }, + "timestamp": "2026-03-19T09:00:25.255379" + }, + "csi": { + "content": [ + "test.paired_end.sorted.bam.csi", + { + "versions_samtools": [ + [ + "SAMTOOLS_INDEX", + "samtools", + "1.23.1" + ] + ] + } + ], + "meta": { + "nf-test": "0.9.3", + "nextflow": "25.10.4" + }, + "timestamp": "2026-03-19T09:00:18.414839" + }, + "crai": { + "content": [ + [ + [ + { + "id": "test", + "single_end": false + }, + "test.paired_end.recalibrated.sorted.cram.crai:md5,14bc3bd5c89cacc8f4541f9062429029" + ] + ], + { + "versions_samtools": [ + [ + "SAMTOOLS_INDEX", + "samtools", + "1.23.1" + ] + ] + } + ], + "meta": { + "nf-test": "0.9.3", + "nextflow": "25.10.4" + }, + "timestamp": "2026-03-19T09:00:13.571297" + }, + "bai": { + "content": [ + [ + [ + { + "id": "test", + "single_end": false + }, + "test.paired_end.sorted.bam.bai:md5,704c10dd1326482448ca3073fdebc2f4" + ] + ], + { + "versions_samtools": [ + [ + "SAMTOOLS_INDEX", + "samtools", + "1.23.1" + ] + ] + } + ], + "meta": { + "nf-test": "0.9.3", + "nextflow": "25.10.4" + }, + "timestamp": "2026-03-19T09:00:06.767362" + } +} \ No newline at end of file diff --git a/modules/nf-core/samtools/sort/environment.yml b/modules/nf-core/samtools/sort/environment.yml new file mode 100644 index 0000000..946bb36 --- /dev/null +++ b/modules/nf-core/samtools/sort/environment.yml @@ -0,0 +1,10 @@ +--- +# yaml-language-server: $schema=https://raw.githubusercontent.com/nf-core/modules/master/modules/environment-schema.json +channels: + - conda-forge + - bioconda +dependencies: + # renovate: datasource=conda depName=bioconda/htslib + - bioconda::htslib=1.23.1 + # renovate: datasource=conda depName=bioconda/samtools + - bioconda::samtools=1.23.1 diff --git a/modules/nf-core/samtools/sort/main.nf b/modules/nf-core/samtools/sort/main.nf new file mode 100644 index 0000000..e75ce91 --- /dev/null +++ b/modules/nf-core/samtools/sort/main.nf @@ -0,0 +1,97 @@ +process SAMTOOLS_SORT { + tag "${meta.id}" + label 'process_medium' + + conda "${moduleDir}/environment.yml" + container "${workflow.containerEngine in ['singularity', 'apptainer'] && !task.ext.singularity_pull_docker_container + ? 'https://community-cr-prod.seqera.io/docker/registry/v2/blobs/sha256/8c/8c5d2818c8b9f58e1fba77ce219fdaf32087ae53e857c4a496402978af26e78c/data' + : 'community.wave.seqera.io/library/htslib_samtools:1.23.1--5b6bb4ede7e612e5'}" + + input: + tuple val(meta), path(bam) + tuple val(meta2), path(fasta), path(fai) + val index_format + + output: + tuple val(meta), path("${prefix}.bam"), emit: bam, optional: true + tuple val(meta), path("${prefix}.cram"), emit: cram, optional: true + tuple val(meta), path("${prefix}.sam"), emit: sam, optional: true + tuple val(meta), path("${prefix}.${extension}.{crai,csi,bai}"), emit: index, optional: true + tuple val("${task.process}"), val('samtools'), eval("samtools version | sed '1!d;s/.* //'"), topic: versions, emit: versions_samtools + + when: + task.ext.when == null || task.ext.when + + script: + def args = task.ext.args ?: '' + prefix = task.ext.prefix ?: "${meta.id}" + extension = args.contains("--output-fmt sam") + ? "sam" + : args.contains("--output-fmt cram") + ? "cram" + : "bam" + def reference = fasta ? "--reference ${fasta}" : "" + //setting default values + def write_index = "" + def output_file = "${prefix}.${extension}" + + // Update if index is requested + if (index_format != '' && index_format) { + write_index = "--write-index" + output_file = "${prefix}.${extension}##idx##${prefix}.${extension}.${index_format}" + } + def is_sam = (bam instanceof List ? bam[0] : bam).name.endsWith('.sam') + if (index_format) { + if (!index_format.matches('bai|csi|crai')) { + error("Index format not one of bai, csi, crai.") + } + else if (extension == "sam") { + error("Indexing not compatible with SAM output") + } + } + if ("${bam}" == "${prefix}.bam") { + error("Input and output names are the same, use \"task.ext.prefix\" to disambiguate!") + } + if ("${bam}" == "${prefix}.bam") { + error("Input and output names are the same, use \"task.ext.prefix\" to disambiguate!") + } + + def input_source = is_sam ? "${bam}" : "-" + def pre_command = is_sam ? "" : "samtools cat ${bam} | " + + """ + ${pre_command}samtools sort \\ + ${args} \\ + -T ${prefix} \\ + --threads ${task.cpus} \\ + ${reference} \\ + -o ${output_file} \\ + ${write_index} \\ + ${input_source} + """ + + stub: + def args = task.ext.args ?: '' + prefix = task.ext.prefix ?: "${meta.id}" + extension = args.contains("--output-fmt sam") + ? "sam" + : args.contains("--output-fmt cram") + ? "cram" + : "bam" + + if (index_format) { + if (!index_format.matches('bai|csi|crai')) { + error("Index format not one of bai, csi, crai.") + } + else if (extension == "sam") { + error("Indexing not compatible with SAM output") + } + } + + index = index_format ? "touch ${prefix}.${extension}.${index_format}" : "" + + """ + touch ${prefix}.${extension} + ${index} + """ +} diff --git a/modules/nf-core/samtools/sort/meta.yml b/modules/nf-core/samtools/sort/meta.yml new file mode 100644 index 0000000..0447a95 --- /dev/null +++ b/modules/nf-core/samtools/sort/meta.yml @@ -0,0 +1,126 @@ +name: samtools_sort +description: Sort SAM/BAM/CRAM file +keywords: + - sort + - bam + - sam + - cram +tools: + - samtools: + description: | + SAMtools is a set of utilities for interacting with and post-processing + short DNA sequence read alignments in the SAM, BAM and CRAM formats, written by Heng Li. + These files are generated as output by short read aligners like BWA. + homepage: http://www.htslib.org/ + documentation: http://www.htslib.org/doc/samtools.html + doi: 10.1093/bioinformatics/btp352 + licence: ["MIT"] + identifier: biotools:samtools +input: + - - meta: + type: map + description: | + Groovy Map containing sample information + e.g. [ id:'test', single_end:false ] + - bam: + type: file + description: BAM/CRAM/SAM file(s) + pattern: "*.{bam,cram,sam}" + ontologies: [] + - - meta2: + type: map + description: | + Groovy Map containing reference information + e.g. [ id:'genome' ] + - fasta: + type: file + description: Reference genome FASTA file + pattern: "*.{fa,fasta,fna}" + optional: true + ontologies: [] + - fai: + type: file + description: Reference genome FASTA index file + pattern: "*.{fai}" + optional: true + ontologies: [] + - index_format: + type: string + description: Index format to use (optional) + pattern: "bai|csi|crai" +output: + bam: + - - meta: + type: map + description: | + Groovy Map containing sample information + e.g. [ id:'test', single_end:false ] + - "${prefix}.bam": + type: file + description: Sorted BAM file + pattern: "*.{bam}" + ontologies: [] + cram: + - - meta: + type: map + description: | + Groovy Map containing sample information + e.g. [ id:'test', single_end:false ] + - "${prefix}.cram": + type: file + description: Sorted CRAM file + pattern: "*.{cram}" + ontologies: [] + sam: + - - meta: + type: map + description: | + Groovy Map containing sample information + e.g. [ id:'test', single_end:false ] + - "${prefix}.sam": + type: file + description: Sorted SAM file + pattern: "*.{sam}" + ontologies: [] + index: + - - meta: + type: map + description: | + Groovy Map containing sample information + e.g. [ id:'test', single_end:false ] + - "${prefix}.${extension}.{crai,csi,bai}": + type: file + description: CRAM index file (optional) + pattern: "*.{crai,csi,bai}" + ontologies: [] + versions_samtools: + - - ${task.process}: + type: string + description: The process the versions were collected from + - samtools: + type: string + description: The tool name + - "samtools version | sed '1!d;s/.* //'": + type: string + description: The command used to generate the version of the tool + +topics: + versions: + - - ${task.process}: + type: string + description: The process the versions were collected from + - samtools: + type: string + description: The tool name + - "samtools version | sed '1!d;s/.* //'": + type: string + description: The command used to generate the version of the tool + +authors: + - "@drpatelh" + - "@ewels" + - "@matthdsm" +maintainers: + - "@drpatelh" + - "@ewels" + - "@matthdsm" diff --git a/modules/nf-core/samtools/sort/tests/main.nf.test b/modules/nf-core/samtools/sort/tests/main.nf.test new file mode 100644 index 0000000..b60edf8 --- /dev/null +++ b/modules/nf-core/samtools/sort/tests/main.nf.test @@ -0,0 +1,410 @@ +nextflow_process { + + name "Test Process SAMTOOLS_SORT" + script "../main.nf" + process "SAMTOOLS_SORT" + tag "modules" + tag "modules_nfcore" + tag "samtools" + tag "samtools/sort" + + test("bam_no_index") { + + config "./nextflow.config" + + when { + process { + """ + input[0] = Channel.of([ + [ id:'test', single_end:false ], // meta map + file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/bam/test.paired_end.bam', checkIfExists: true) + ]) + input[1] = Channel.of([ + [ id:'fasta' ], // meta map + file(params.modules_testdata_base_path + 'genomics/sarscov2/genome/genome.fasta', checkIfExists: true), + file(params.modules_testdata_base_path + 'genomics/sarscov2/genome/genome.fasta.fai', checkIfExists: true) + ]) + input[2] = '' + """ + } + } + + then { + assertAll ( + { assert process.success }, + { assert snapshot( + process.out.bam, + process.out.index, + process.out.findAll { key, val -> key.startsWith("versions") } + ).match()} + ) + } + } + + test("bam_bai_index") { + + config "./nextflow.config" + + when { + process { + """ + input[0] = Channel.of([ + [ id:'test', single_end:false ], // meta map + file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/bam/test.paired_end.bam', checkIfExists: true) + ]) + input[1] = Channel.of([ + [ id:'fasta' ], // meta map + file(params.modules_testdata_base_path + 'genomics/sarscov2/genome/genome.fasta', checkIfExists: true), + file(params.modules_testdata_base_path + 'genomics/sarscov2/genome/genome.fasta.fai', checkIfExists: true) + ]) + input[2] = 'bai' + """ + } + } + + then { + assertAll ( + { assert process.success }, + { assert snapshot( + process.out.bam, + process.out.index, + process.out.findAll { key, val -> key.startsWith("versions") } + ).match()} + ) + } + } + + test("bam_csi_index") { + + config "./nextflow.config" + + when { + process { + """ + input[0] = Channel.of([ + [ id:'test', single_end:false ], // meta map + file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/bam/test.paired_end.bam', checkIfExists: true) + ]) + input[1] = Channel.of([ + [ id:'fasta' ], // meta map + file(params.modules_testdata_base_path + 'genomics/sarscov2/genome/genome.fasta', checkIfExists: true), + file(params.modules_testdata_base_path + 'genomics/sarscov2/genome/genome.fasta.fai', checkIfExists: true) + ]) + input[2] = 'csi' + """ + } + } + + then { + assertAll ( + { assert process.success }, + { assert snapshot( + process.out.bam, + process.out.index, + process.out.findAll { key, val -> key.startsWith("versions") } + ).match()} + ) + } + } + + test("multiple bam") { + + config "./nextflow.config" + + when { + process { + """ + input[0] = Channel.of([ + [ id:'test', single_end:false ], // meta map + [ + file(params.modules_testdata_base_path + 'genomics/homo_sapiens/illumina/bam/test.paired_end.sorted.bam', checkIfExists: true), + file(params.modules_testdata_base_path + 'genomics/homo_sapiens/illumina/bam/test2.paired_end.sorted.bam', checkIfExists: true) + ] + ]) + input[1] = Channel.of([ + [ id:'fasta' ], // meta map + file(params.modules_testdata_base_path + 'genomics/homo_sapiens/genome/genome.fasta', checkIfExists: true), + file(params.modules_testdata_base_path + 'genomics/homo_sapiens/genome/genome.fasta.fai', checkIfExists: true) + ]) + input[2] = '' + """ + } + } + + then { + assertAll ( + { assert process.success }, + { assert snapshot( + process.out.bam, + process.out.index.collect { it.collect { it instanceof Map ? it : file(it).name } }, + process.out.findAll { key, val -> key.startsWith("versions") } + ).match()} + ) + } + } + + test("multiple bam bai index") { + + config "./nextflow.config" + + when { + process { + """ + input[0] = Channel.of([ + [ id:'test', single_end:false ], // meta map + [ + file(params.modules_testdata_base_path + 'genomics/homo_sapiens/illumina/bam/test.paired_end.sorted.bam', checkIfExists: true), + file(params.modules_testdata_base_path + 'genomics/homo_sapiens/illumina/bam/test2.paired_end.sorted.bam', checkIfExists: true) + ] + ]) + input[1] = Channel.of([ + [ id:'fasta' ], // meta map + file(params.modules_testdata_base_path + 'genomics/homo_sapiens/genome/genome.fasta', checkIfExists: true), + file(params.modules_testdata_base_path + 'genomics/homo_sapiens/genome/genome.fasta.fai', checkIfExists: true) + ]) + input[2] = 'bai' + """ + } + } + + then { + assertAll ( + { assert process.success }, + { assert snapshot( + process.out.bam, + process.out.index.collect { it.collect { it instanceof Map ? it : file(it).name } }, + process.out.findAll { key, val -> key.startsWith("versions") } + ).match()} + ) + } + } + + test("multiple bam csi index") { + + config "./nextflow.config" + + when { + process { + """ + input[0] = Channel.of([ + [ id:'test', single_end:false ], // meta map + [ + file(params.modules_testdata_base_path + 'genomics/homo_sapiens/illumina/bam/test.paired_end.sorted.bam', checkIfExists: true), + file(params.modules_testdata_base_path + 'genomics/homo_sapiens/illumina/bam/test2.paired_end.sorted.bam', checkIfExists: true) + ] + ]) + input[1] = Channel.of([ + [ id:'fasta' ], // meta map + file(params.modules_testdata_base_path + 'genomics/homo_sapiens/genome/genome.fasta', checkIfExists: true), + file(params.modules_testdata_base_path + 'genomics/homo_sapiens/genome/genome.fasta.fai', checkIfExists: true) + ]) + input[2] = 'csi' + """ + } + } + + then { + assertAll ( + { assert process.success }, + { assert snapshot( + process.out.bam, + process.out.index.collect { it.collect { it instanceof Map ? it : file(it).name } }, + process.out.findAll { key, val -> key.startsWith("versions") } + ).match()} + ) + } + } + + test("cram") { + + config "./nextflow_cram.config" + + when { + process { + """ + input[0] = Channel.of([ + [ id:'test', single_end:false ], // meta map + file(params.modules_testdata_base_path + 'genomics/homo_sapiens/illumina/cram/test.paired_end.sorted.cram', checkIfExists: true) + ]) + input[1] = Channel.of([ + [ id:'fasta' ], // meta map + file(params.modules_testdata_base_path + 'genomics/homo_sapiens/genome/genome.fasta', checkIfExists: true), + file(params.modules_testdata_base_path + 'genomics/homo_sapiens/genome/genome.fasta.fai', checkIfExists: true) + ]) + input[2] = '' + """ + } + } + + then { + assertAll ( + { assert process.success }, + { assert snapshot( + process.out.cram.collect { it.collect { it instanceof Map ? it : file(it).name } }, + process.out.index.collect { it.collect { it instanceof Map ? it : file(it).name } }, + process.out.findAll { key, val -> key.startsWith("versions") } + ).match()} + ) + } + } + + test("bam - stub") { + + options "-stub" + config "./nextflow.config" + + when { + process { + """ + input[0] = Channel.of([ + [ id:'test', single_end:false ], // meta map + file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/bam/test.paired_end.bam', checkIfExists: true) + ]) + input[1] = Channel.of([ + [ id:'fasta' ], // meta map + file(params.modules_testdata_base_path + 'genomics/sarscov2/genome/genome.fasta', checkIfExists: true), + file(params.modules_testdata_base_path + 'genomics/sarscov2/genome/genome.fasta.fai', checkIfExists: true) + ]) + input[2] = '' + """ + } + } + + then { + assertAll ( + { assert process.success }, + { assert snapshot(process.out.findAll { key, val -> key.startsWith("versions") }).match() } + ) + } + } + + test("multiple bam - stub") { + + config "./nextflow.config" + + when { + process { + """ + input[0] = Channel.of([ + [ id:'test', single_end:false ], // meta map + [ + file(params.modules_testdata_base_path + 'genomics/homo_sapiens/illumina/bam/test.paired_end.sorted.bam', checkIfExists: true), + file(params.modules_testdata_base_path + 'genomics/homo_sapiens/illumina/bam/test2.paired_end.sorted.bam', checkIfExists: true) + ] + ]) + input[1] = Channel.of([ + [ id:'fasta' ], // meta map + file(params.modules_testdata_base_path + 'genomics/homo_sapiens/genome/genome.fasta', checkIfExists: true), + file(params.modules_testdata_base_path + 'genomics/homo_sapiens/genome/genome.fasta.fai', checkIfExists: true) + ]) + input[2] = '' + """ + } + } + + then { + assertAll ( + { assert process.success }, + { assert snapshot(process.out.findAll { key, val -> key.startsWith("versions") }).match() } + ) + } + } + + test("cram - stub") { + + options "-stub" + config "./nextflow_cram.config" + + when { + process { + """ + input[0] = Channel.of([ + [ id:'test', single_end:false ], // meta map + file(params.modules_testdata_base_path + 'genomics/homo_sapiens/illumina/cram/test.paired_end.sorted.cram', checkIfExists: true) + ]) + input[1] = Channel.of([ + [ id:'fasta' ], // meta map + file(params.modules_testdata_base_path + 'genomics/homo_sapiens/genome/genome.fasta', checkIfExists: true), + file(params.modules_testdata_base_path + 'genomics/homo_sapiens/genome/genome.fasta.fai', checkIfExists: true) + ]) + input[2] = '' + """ + } + } + + then { + assertAll ( + { assert process.success }, + { assert snapshot(process.out.findAll { key, val -> key.startsWith("versions") }).match() } + ) + } + } + + test("multi_sam") { + + config "./nextflow_sam.config" + + when { + process { + """ + input[0] = Channel.of([ + [ id:'test', single_end:false ], // meta map + file(params.modules_testdata_base_path + 'genomics/homo_sapiens/pairtools/mock.sam', checkIfExists: true) + ]) + input[1] = Channel.of([ + [ id:'fasta' ], // meta map + file(params.modules_testdata_base_path + 'genomics/homo_sapiens/genome/genome.fasta', checkIfExists: true), + file(params.modules_testdata_base_path + 'genomics/homo_sapiens/genome/genome.fasta.fai', checkIfExists: true) + ]) + input[2] = '' + """ + } + } + + then { + assertAll ( + { assert process.success }, + { assert snapshot( + process.out.bam.collect { it.collect { it instanceof Map ? it : file(it).name } }, + process.out.index.collect { it.collect { it instanceof Map ? it : file(it).name } }, + process.out.findAll { key, val -> key.startsWith("versions") } + ).match()} + ) + } + } + + + test("sam") { + + config "./nextflow_sam.config" + + when { + process { + """ + input[0] = Channel.of([ + [ id:'test', single_end:false ], // meta map + file(params.modules_testdata_base_path + 'genomics/homo_sapiens/pairtools/mock.sam', checkIfExists: true), + file(params.modules_testdata_base_path + 'genomics/homo_sapiens/pairtools/mock.sam', checkIfExists: true) + ]) + input[1] = Channel.of([ + [ id:'fasta' ], // meta map + file(params.modules_testdata_base_path + 'genomics/homo_sapiens/genome/genome.fasta', checkIfExists: true), + file(params.modules_testdata_base_path + 'genomics/homo_sapiens/genome/genome.fasta.fai', checkIfExists: true) + ]) + input[2] = '' + """ + } + } + + then { + assertAll ( + { assert process.success }, + { assert snapshot( + process.out.bam.collect { it.collect { it instanceof Map ? it : file(it).name } }, + process.out.index.collect { it.collect { it instanceof Map ? it : file(it).name } }, + process.out.findAll { key, val -> key.startsWith("versions") } + ).match()} + ) + } + } +} diff --git a/modules/nf-core/samtools/sort/tests/main.nf.test.snap b/modules/nf-core/samtools/sort/tests/main.nf.test.snap new file mode 100644 index 0000000..5ce05c3 --- /dev/null +++ b/modules/nf-core/samtools/sort/tests/main.nf.test.snap @@ -0,0 +1,344 @@ +{ + "cram": { + "content": [ + [ + [ + { + "id": "test", + "single_end": false + }, + "test.sorted.cram" + ] + ], + [ + [ + { + "id": "test", + "single_end": false + }, + "test.sorted.cram.crai" + ] + ], + { + "versions_samtools": [ + [ + "SAMTOOLS_SORT", + "samtools", + "1.23.1" + ] + ] + } + ], + "meta": { + "nf-test": "0.9.3", + "nextflow": "25.10.4" + }, + "timestamp": "2026-03-19T09:04:36.491063" + }, + "bam_csi_index": { + "content": [ + [ + [ + { + "id": "test", + "single_end": false + }, + "test.sorted.bam:md5,53aea06779611856bc481c60beabecaa" + ] + ], + [ + [ + { + "id": "test", + "single_end": false + }, + "test.sorted.bam.csi:md5,f77a4adb3dde616d7eafd28db2ed147c" + ] + ], + { + "versions_samtools": [ + [ + "SAMTOOLS_SORT", + "samtools", + "1.23.1" + ] + ] + } + ], + "meta": { + "nf-test": "0.9.3", + "nextflow": "25.10.4" + }, + "timestamp": "2026-03-19T09:04:14.341977" + }, + "bam - stub": { + "content": [ + { + "versions_samtools": [ + [ + "SAMTOOLS_SORT", + "samtools", + "1.23.1" + ] + ] + } + ], + "meta": { + "nf-test": "0.9.3", + "nextflow": "25.10.4" + }, + "timestamp": "2026-03-19T09:04:43.558376" + }, + "multiple bam bai index": { + "content": [ + [ + [ + { + "id": "test", + "single_end": false + }, + "test.sorted.bam:md5,a15f775c655d4a3b080812a8aae84d34" + ] + ], + [ + [ + { + "id": "test", + "single_end": false + }, + "test.sorted.bam.bai" + ] + ], + { + "versions_samtools": [ + [ + "SAMTOOLS_SORT", + "samtools", + "1.23.1" + ] + ] + } + ], + "meta": { + "nf-test": "0.9.3", + "nextflow": "25.10.4" + }, + "timestamp": "2026-03-19T09:04:25.647565" + }, + "cram - stub": { + "content": [ + { + "versions_samtools": [ + [ + "SAMTOOLS_SORT", + "samtools", + "1.23.1" + ] + ] + } + ], + "meta": { + "nf-test": "0.9.3", + "nextflow": "25.10.4" + }, + "timestamp": "2026-03-19T09:04:54.684578" + }, + "multiple bam": { + "content": [ + [ + [ + { + "id": "test", + "single_end": false + }, + "test.sorted.bam:md5,f4343475d9ed2c261f31e1e49d67c1b6" + ] + ], + [ + + ], + { + "versions_samtools": [ + [ + "SAMTOOLS_SORT", + "samtools", + "1.23.1" + ] + ] + } + ], + "meta": { + "nf-test": "0.9.3", + "nextflow": "25.10.4" + }, + "timestamp": "2026-03-19T09:04:20.2368" + }, + "multiple bam - stub": { + "content": [ + { + "versions_samtools": [ + [ + "SAMTOOLS_SORT", + "samtools", + "1.23.1" + ] + ] + } + ], + "meta": { + "nf-test": "0.9.3", + "nextflow": "25.10.4" + }, + "timestamp": "2026-03-19T09:04:48.874947" + }, + "bam_no_index": { + "content": [ + [ + [ + { + "id": "test", + "single_end": false + }, + "test.sorted.bam:md5,9277ba4bea590ae1b84e6ab06d11d79b" + ] + ], + [ + + ], + { + "versions_samtools": [ + [ + "SAMTOOLS_SORT", + "samtools", + "1.23.1" + ] + ] + } + ], + "meta": { + "nf-test": "0.9.3", + "nextflow": "25.10.4" + }, + "timestamp": "2026-03-19T09:04:03.721646" + }, + "multi_sam": { + "content": [ + [ + + ], + [ + + ], + { + "versions_samtools": [ + [ + "SAMTOOLS_SORT", + "samtools", + "1.23.1" + ] + ] + } + ], + "meta": { + "nf-test": "0.9.3", + "nextflow": "25.10.4" + }, + "timestamp": "2026-03-19T09:05:00.624092" + }, + "multiple bam csi index": { + "content": [ + [ + [ + { + "id": "test", + "single_end": false + }, + "test.sorted.bam:md5,f168809dc154156c40548c06d0f46791" + ] + ], + [ + [ + { + "id": "test", + "single_end": false + }, + "test.sorted.bam.csi" + ] + ], + { + "versions_samtools": [ + [ + "SAMTOOLS_SORT", + "samtools", + "1.23.1" + ] + ] + } + ], + "meta": { + "nf-test": "0.9.3", + "nextflow": "25.10.4" + }, + "timestamp": "2026-03-19T09:04:31.11865" + }, + "sam": { + "content": [ + [ + + ], + [ + + ], + { + "versions_samtools": [ + [ + "SAMTOOLS_SORT", + "samtools", + "1.23.1" + ] + ] + } + ], + "meta": { + "nf-test": "0.9.3", + "nextflow": "25.10.4" + }, + "timestamp": "2026-03-19T09:05:06.309319" + }, + "bam_bai_index": { + "content": [ + [ + [ + { + "id": "test", + "single_end": false + }, + "test.sorted.bam:md5,2ca2d7f2368251d3f06f84afa49865a5" + ] + ], + [ + [ + { + "id": "test", + "single_end": false + }, + "test.sorted.bam.bai:md5,66dca3dc2e314029035799f6f44f60d1" + ] + ], + { + "versions_samtools": [ + [ + "SAMTOOLS_SORT", + "samtools", + "1.23.1" + ] + ] + } + ], + "meta": { + "nf-test": "0.9.3", + "nextflow": "25.10.4" + }, + "timestamp": "2026-03-19T09:04:09.147615" + } +} \ No newline at end of file diff --git a/modules/nf-core/samtools/sort/tests/nextflow.config b/modules/nf-core/samtools/sort/tests/nextflow.config new file mode 100644 index 0000000..ca694ca --- /dev/null +++ b/modules/nf-core/samtools/sort/tests/nextflow.config @@ -0,0 +1,6 @@ +process { + + withName: SAMTOOLS_SORT { + ext.prefix = { "${meta.id}.sorted" } + } +} diff --git a/modules/nf-core/samtools/sort/tests/nextflow_cram.config b/modules/nf-core/samtools/sort/tests/nextflow_cram.config new file mode 100644 index 0000000..8ebc9d9 --- /dev/null +++ b/modules/nf-core/samtools/sort/tests/nextflow_cram.config @@ -0,0 +1,7 @@ +process { + + withName: SAMTOOLS_SORT { + ext.prefix = { "${meta.id}.sorted" } + ext.args = "--write-index --output-fmt cram" + } +} diff --git a/modules/nf-core/samtools/sort/tests/nextflow_sam.config b/modules/nf-core/samtools/sort/tests/nextflow_sam.config new file mode 100644 index 0000000..29ee6a8 --- /dev/null +++ b/modules/nf-core/samtools/sort/tests/nextflow_sam.config @@ -0,0 +1,7 @@ +process { + + withName: SAMTOOLS_SORT { + ext.prefix = { "${meta.id}.sorted" } + ext.args = "--output-fmt sam" + } +} diff --git a/nextflow.config b/nextflow.config index 0e4332b..beb79d6 100644 --- a/nextflow.config +++ b/nextflow.config @@ -9,14 +9,29 @@ // Global default params, used in configs params { - // TODO nf-core: Specify your pipeline's command line flags - // Input options input = null + min_counts = 10 + base_qual = 30 + min_flank = 2 + error_correction = 'false_doubles' + false_doubles_method = 'mle' + false_doubles_codon_window = 40 + pdb = null + mutagenesis_type = 'nnk' + run_seqdepth = true + reading_frame = null + custom_codon_library = '/NULL' + sliding_window_size = 10 + aimed_cov = 100 + fitness = false + dimsum = false + mutscan = false + // References genome = null igenomes_base = 's3://ngi-igenomes/igenomes/' - igenomes_ignore = false + igenomes_ignore = true // MultiQC options multiqc_config = null @@ -98,7 +113,6 @@ profiles { } arm64 { process.arch = 'arm64' - // TODO https://github.com/nf-core/modules/issues/6694 // For now if you're using arm64 you have to use wave for the sake of the maintainers // wave profile apptainer.ociAutoPull = true @@ -181,8 +195,7 @@ includeConfig params.custom_config_base && (!System.getenv('NXF_OFFLINE') || !pa // Load nf-core/deepmutscan custom profiles from different institutions. -// TODO nf-core: Optionally, you can add a pipeline-specific nf-core config at https://github.com/nf-core/configs -// includeConfig params.custom_config_base && (!System.getenv('NXF_OFFLINE') || !params.custom_config_base.startsWith('http')) ? "${params.custom_config_base}/pipeline/deepmutscan.config" : "/dev/null" +includeConfig params.custom_config_base && (!System.getenv('NXF_OFFLINE') || !params.custom_config_base.startsWith('http')) ? "${params.custom_config_base}/pipeline/deepmutscan.config" : "/dev/null" // Set default registry for Apptainer, Docker, Podman, Charliecloud and Singularity independent of -profile // Will not be used unless Apptainer / Docker / Podman / Charliecloud / Singularity are enabled @@ -240,7 +253,6 @@ dag { manifest { name = 'nf-core/deepmutscan' contributors = [ - // TODO nf-core: Update the field with the details of the contributors to your pipeline. New with Nextflow version 24.10.0 [ name: 'Benjamin Wehnert & Max Stammnitz', affiliation: '', @@ -264,9 +276,6 @@ plugins { id 'nf-schema@2.5.1' // Validation of pipeline parameters and creation of an input channel from a sample sheet } -validation { - defaultIgnoreParams = ["genomes"] - monochromeLogs = params.monochrome_logs -} + // Load modules.config for DSL2 module specific options includeConfig 'conf/modules.config' diff --git a/nextflow_schema.json b/nextflow_schema.json index 40d7d83..dfe43e8 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -2,7 +2,7 @@ "$schema": "https://json-schema.org/draft/2020-12/schema", "$id": "https://raw.githubusercontent.com/nf-core/deepmutscan/master/nextflow_schema.json", "title": "nf-core/deepmutscan pipeline parameters", - "description": "Until now, most Deep Mutational Scanning (DMS) experiments relied on variant-specific barcoded libraries for sequencing. This method enabled DMS on large proteins and led to many great publications. Recently, efforts have increased to make use of the classic and more simple random fragmentation-based short-read sequencing (“shotgun-sequencing”). This saves time and money and due to its simpler experimental design is less prone to mistakes. dmscore handles the essential computational steps, processing the raw FASTQ files and generating a count table of variants. Along the way, it provides multiple QC metrics, enabling users to quickly evaluate the success of their experimental setup.", + "description": "Until now, most Deep Mutational Scanning (DMS) experiments relied on variant-specific barcoded libraries for sequencing. This method enabled DMS on large proteins and led to many great publications. Recently, efforts have increased to make use of the classic and more simple random fragmentation-based short-read sequencing (\u201cshotgun-sequencing\u201d). This saves time and money and due to its simpler experimental design is less prone to mistakes. dmscore handles the essential computational steps, processing the raw FASTQ files and generating a count table of variants. Along the way, it provides multiple QC metrics, enabling users to quickly evaluate the success of their experimental setup.", "type": "object", "$defs": { "input_output_options": { @@ -10,7 +10,7 @@ "type": "object", "fa_icon": "fas fa-terminal", "description": "Define where the pipeline should find input data and save output data.", - "required": ["input", "outdir"], + "required": ["input", "outdir", "fasta", "reading_frame"], "properties": { "input": { "type": "string", @@ -40,6 +40,93 @@ "type": "string", "description": "MultiQC report title. Printed as page header, used for filename if not otherwise specified.", "fa_icon": "fas fa-file-signature" + }, + "reading_frame": { + "type": "string", + "description": "Start and stop codon positions in the format 'start-stop', e.g., '352-1383'.", + "pattern": "^\\d+-\\d+$" + }, + "min_counts": { + "type": "integer", + "description": "minimum counts for variant to be recognized. All variants below min_counts will be set to 0", + "minimum": 1, + "default": 10 + }, + "base_qual": { + "type": "integer", + "description": "Minimum base quality (Phred) for a mutation to be counted by the variant counter.", + "minimum": 0, + "default": 30 + }, + "min_flank": { + "type": "integer", + "description": "Minimum distance (nt) a mutation or covered base must be from a read edge; also excludes those bases from coverage. Set to 0 to disable edge trimming.", + "minimum": 0, + "default": 2 + }, + "error_correction": { + "type": "string", + "description": "Sequencing-error correction of single-codon variant counts. 'false_doubles' (default) treats observed multi-codon variants as errors to estimate the per-nucleotide error rate; 'none' disables correction; 'wildtype' subtracts the position-specific error profile measured from an additional deep wildtype-only sample (requires samplesheet rows with type 'wildtype').", + "enum": ["false_doubles", "none", "wildtype"], + "default": "false_doubles" + }, + "false_doubles_method": { + "type": "string", + "description": "Estimator used by the false-doubles correction. 'mle' (default) is a per-variant maximum-likelihood error rate; 'eb' is an empirical-Bayes estimate that pools across variants and shrinks per substitution class (steadier for sparse variants). Only applies when error_correction = 'false_doubles'.", + "enum": ["mle", "eb"], + "default": "mle" + }, + "false_doubles_codon_window": { + "type": "integer", + "description": "Window (in codons, upstream and downstream) within which false double mutants are matched to a single-nucleotide variant during false-doubles correction.", + "minimum": 1, + "default": 40 + }, + "pdb": { + "type": "string", + "format": "file-path", + "exists": true, + "mimetype": "chemical/x-pdb", + "description": "Optional wildtype 3D structure (PDB). When supplied together with `--fitness`, an interactive variant effect inspection tool (self-contained HTML) is built that projects fitness, counts and error-correction biases onto the structure.", + "help_text": "Use a crystal structure, an AlphaFold DB model, or any precomputed PDB whose sequence matches the wildtype ORF. Structure prediction (nf-core/proteinfold) is not wired in this release." + }, + "sliding_window_size": { + "type": "integer", + "description": "To flatten graphs in plots (e.g. `GLOBAL_POS_BIASES_COUNTS` function)", + "default": 10 + }, + "aimed_cov": { + "type": "integer", + "description": "aimed coverage (assuming equal spread) to visualize threshold in plots", + "default": 100 + }, + "mutagenesis_type": { + "type": "string", + "description": "Type of mutagenic primers. Choose from nnk, nns, nnh, nnn, nnk_nns, nnk_nns_nnh, custom. When using 'custom', also provide the parameter 'custom_codon_library'.", + "default": "nnk" + }, + "custom_codon_library": { + "type": "string", + "format": "file-path", + "description": "Path to a file defining a custom codon library. Required when mutagenesis_type is set to `custom`. The script auto-detects the format: provide either a global comma-separated list of codons (e.g., 'AAA,AAC,AAG') OR a position-specific, headerless list where each row specifies the target sequence position followed by its allowed codons (e.g., '1,ACG,AAA,ACA' and '2,AAA,TTT,ACA' on separate lines). Both as .csv.", + "default": "/NULL" + }, + "dimsum": { + "type": ["boolean", "string"], + "description": "Run DiMSum for fitness/functionality scores from growth-based selection input & output samples" + }, + "mutscan": { + "type": ["boolean", "string"], + "description": "Run mutscan (Soneson et al., 2023) to estimate fitness/functionality scores from input & output samples" + }, + "fitness": { + "type": ["boolean", "string"], + "description": "Enable basic fitness calculation and preceded data preparation." + }, + "run_seqdepth": { + "type": "boolean", + "default": true, + "description": "Estimate sequencing-depth saturation by closed-form hypergeometric rarefaction (fast; on by default)." } } }, @@ -217,10 +304,6 @@ "description": "Suffix to add to the trace report filename. Default is the date and time in the format yyyy-MM-dd_HH-mm-ss.", "hidden": true }, - "help": { - "type": ["boolean", "string"], - "description": "Display the help message." - }, "help_full": { "type": "boolean", "description": "Display the full detailed help message." diff --git a/nf-test.config b/nf-test.config index f7aaeb4..c0c14da 100644 --- a/nf-test.config +++ b/nf-test.config @@ -34,5 +34,6 @@ config { // load the necessary plugins plugins { load "nft-utils@0.0.3" + load "nft-csv@0.1.0" } } diff --git a/ro-crate-metadata.json b/ro-crate-metadata.json index d0aa84b..1a0a29a 100644 --- a/ro-crate-metadata.json +++ b/ro-crate-metadata.json @@ -22,8 +22,8 @@ "@id": "./", "@type": "Dataset", "creativeWorkStatus": "Stable", - "datePublished": "2026-04-30T13:32:46+00:00", - "description": "

\n \n \n \"nf-core/deepmutscan\"\n \n

\n\n[![Open in GitHub Codespaces](https://img.shields.io/badge/Open_In_GitHub_Codespaces-black?labelColor=grey&logo=github)](https://github.com/codespaces/new/nf-core/deepmutscan)\n[![GitHub Actions CI Status](https://github.com/nf-core/deepmutscan/actions/workflows/nf-test.yml/badge.svg)](https://github.com/nf-core/deepmutscan/actions/workflows/nf-test.yml)\n[![GitHub Actions Linting Status](https://github.com/nf-core/deepmutscan/actions/workflows/linting.yml/badge.svg)](https://github.com/nf-core/deepmutscan/actions/workflows/linting.yml)[![AWS CI](https://img.shields.io/badge/CI%20tests-full%20size-FF9900?labelColor=000000&logo=Amazon%20AWS)](https://nf-co.re/deepmutscan/results)[![Cite with Zenodo](http://img.shields.io/badge/DOI-10.5281/zenodo.XXXXXXX-1073c8?labelColor=000000)](https://doi.org/10.5281/zenodo.XXXXXXX)\n[![nf-test](https://img.shields.io/badge/unit_tests-nf--test-337ab7.svg)](https://www.nf-test.com)\n\n[![Nextflow](https://img.shields.io/badge/version-%E2%89%A525.10.4-green?style=flat&logo=nextflow&logoColor=white&color=%230DC09D&link=https%3A%2F%2Fnextflow.io)](https://www.nextflow.io/)\n[![nf-core template version](https://img.shields.io/badge/nf--core_template-4.0.2-green?style=flat&logo=nfcore&logoColor=white&color=%2324B064&link=https%3A%2F%2Fnf-co.re)](https://github.com/nf-core/tools/releases/tag/4.0.2)\n[![run with conda](http://img.shields.io/badge/run%20with-conda-3EB049?labelColor=000000&logo=anaconda)](https://docs.conda.io/en/latest/)\n[![run with docker](https://img.shields.io/badge/run%20with-docker-0db7ed?labelColor=000000&logo=docker)](https://www.docker.com/)\n[![run with singularity](https://img.shields.io/badge/run%20with-singularity-1d355c.svg?labelColor=000000)](https://sylabs.io/docs/)\n[![Launch on Seqera Platform](https://img.shields.io/badge/Launch%20%F0%9F%9A%80-Seqera%20Platform-%234256e7)](https://cloud.seqera.io/launch?pipeline=https://github.com/nf-core/deepmutscan)\n\n[![Get help on Slack](http://img.shields.io/badge/slack-nf--core%20%23deepmutscan-4A154B?labelColor=000000&logo=slack)](https://nfcore.slack.com/channels/deepmutscan)[![Follow on Bluesky](https://img.shields.io/badge/bluesky-%40nf__core-1185fe?labelColor=000000&logo=bluesky)](https://bsky.app/profile/nf-co.re)[![Follow on Mastodon](https://img.shields.io/badge/mastodon-nf__core-6364ff?labelColor=FFFFFF&logo=mastodon)](https://mstdn.science/@nf_core)[![Watch on YouTube](http://img.shields.io/badge/youtube-nf--core-FF0000?labelColor=000000&logo=youtube)](https://www.youtube.com/c/nf-core)\n\n## Introduction\n\n**nf-core/deepmutscan** is a bioinformatics pipeline that ...\n\n\n\n\n1. Read QC ([`FastQC`](https://www.bioinformatics.babraham.ac.uk/projects/fastqc/))2. Present QC for raw reads ([`MultiQC`](http://multiqc.info/))\n\n## Usage\n\n> [!NOTE]\n> If you are new to Nextflow and nf-core, please refer to [this page](https://nf-co.re/docs/get_started/environment_setup/overview) on how to set-up Nextflow. Make sure to [test your setup](https://nf-co.re/docs/get_started/run-your-first-pipeline) with `-profile test` before running the workflow on actual data.\n\n\n\nNow, you can run the pipeline using:\n\n\n\n```bash\nnextflow run nf-core/deepmutscan \\\n -profile \\\n --input samplesheet.csv \\\n --outdir \n```\n\n> [!WARNING]\n> Please provide pipeline parameters via the CLI or Nextflow `-params-file` option. Custom config files including those provided by the `-c` Nextflow option can be used to provide any configuration _**except for parameters**_; see [docs](https://nf-co.re/docs/running/run-pipelines#using-parameter-files).\n\nFor more details and further functionality, please refer to the [usage documentation](https://nf-co.re/deepmutscan/usage) and the [parameter documentation](https://nf-co.re/deepmutscan/parameters).\n\n## Pipeline output\n\nTo see the results of an example test run with a full size dataset refer to the [results](https://nf-co.re/deepmutscan/results) tab on the nf-core website pipeline page.\nFor more details about the output files and reports, please refer to the\n[output documentation](https://nf-co.re/deepmutscan/output).\n\n## Credits\n\nnf-core/deepmutscan was originally written by Benjamin Wehnert & Max Stammnitz.\n\nWe thank the following people for their extensive assistance in the development of this pipeline:\n\n\n\n## Contributions and Support\n\nIf you would like to contribute to this pipeline, please see the [contributing guidelines](docs/CONTRIBUTING.md).\n\nFor further information or help, don't hesitate to get in touch on the [Slack `#deepmutscan` channel](https://nfcore.slack.com/channels/deepmutscan) (you can join with [this invite](https://nf-co.re/join/slack)).\n\n## Citations\n\n\n\n\n\n\nAn extensive list of references for the tools used by the pipeline can be found in the [`CITATIONS.md`](CITATIONS.md) file.\n\nYou can cite the `nf-core` publication as follows:\n\n> **The nf-core framework for community-curated bioinformatics pipelines.**\n>\n> Philip Ewels, Alexander Peltzer, Sven Fillinger, Harshil Patel, Johannes Alneberg, Andreas Wilm, Maxime Ulysse Garcia, Paolo Di Tommaso & Sven Nahnsen.\n>\n> _Nat Biotechnol._ 2020 Feb 13. doi: [10.1038/s41587-020-0439-x](https://dx.doi.org/10.1038/s41587-020-0439-x).\n", + "datePublished": "2026-06-13T18:39:59+00:00", + "description": "

\n \n \n \"nf-core/deepmutscan\"\n \n

\n\n[![GitHub Actions CI Status](https://github.com/nf-core/deepmutscan/actions/workflows/ci.yml/badge.svg)](https://github.com/nf-core/deepmutscan/actions/workflows/ci.yml)\n[![GitHub Actions Linting Status](https://github.com/nf-core/deepmutscan/actions/workflows/linting.yml/badge.svg)](https://github.com/nf-core/deepmutscan/actions/workflows/linting.yml)[![AWS CI](https://img.shields.io/badge/CI%20tests-full%20size-FF9900?labelColor=000000&logo=Amazon%20AWS)](https://nf-co.re/deepmutscan/results)[![Cite with Zenodo](http://img.shields.io/badge/DOI-10.5281/zenodo.XXXXXXX-1073c8?labelColor=000000)](https://doi.org/10.5281/zenodo.XXXXXXX)\n[![nf-test](https://img.shields.io/badge/unit_tests-nf--test-337ab7.svg)](https://www.nf-test.com)\n\n[![Nextflow](https://img.shields.io/badge/version-%E2%89%A525.10.4-green?style=flat&logo=nextflow&logoColor=white&color=%230DC09D&link=https%3A%2F%2Fnextflow.io)](https://www.nextflow.io/)\n[![nf-core template version](https://img.shields.io/badge/nf--core_template-4.0.2-green?style=flat&logo=nfcore&logoColor=white&color=%2324B064&link=https%3A%2F%2Fnf-co.re)](https://github.com/nf-core/tools/releases/tag/4.0.2)\n[![run with conda](http://img.shields.io/badge/run%20with-conda-3EB049?labelColor=000000&logo=anaconda)](https://docs.conda.io/en/latest/)\n[![run with docker](https://img.shields.io/badge/run%20with-docker-0db7ed?labelColor=000000&logo=docker)](https://www.docker.com/)\n[![run with singularity](https://img.shields.io/badge/run%20with-singularity-1d355c.svg?labelColor=000000)](https://sylabs.io/docs/)\n[![Launch on Seqera Platform](https://img.shields.io/badge/Launch%20%F0%9F%9A%80-Seqera%20Platform-%234256e7)](https://cloud.seqera.io/launch?pipeline=https://github.com/nf-core/deepmutscan)\n\n[![Get help on Slack](http://img.shields.io/badge/slack-nf--core%20%23deepmutscan-4A154B?labelColor=000000&logo=slack)](https://nfcore.slack.com/channels/deepmutscan)[![Follow on Twitter](http://img.shields.io/badge/twitter-%40nf__core-1DA1F2?labelColor=000000&logo=twitter)](https://twitter.com/nf_core)[![Follow on Mastodon](https://img.shields.io/badge/mastodon-nf__core-6364ff?labelColor=FFFFFF&logo=mastodon)](https://mstdn.science/@nf_core)[![Watch on YouTube](http://img.shields.io/badge/youtube-nf--core-FF0000?labelColor=000000&logo=youtube)](https://www.youtube.com/c/nf-core)\n\n## Introduction\n\n**nf-core/deepmutscan** is a workflow designed for the analysis of deep mutational scanning (DMS) data. DMS enables researchers to experimentally measure the fitness effects of thousands of genes or gene variants simultaneously, helping to classify disease causing mutants in human and animal populations, to learn the fundamental rules of protein architecture, small-molecule binding, mRNA splicing, viral evolution and many other quantifiable phenotypes.\n\nWhile DNA synthesis and sequencing technologies have advanced substantially, long open reading frame (ORF) targets still present a major challenge for DMS studies. Shotgun DNA sequencing can be used to greatly speed up the inference of long ORF mutant fitness landscapes, theoretically at no expense in accuracy. We have designed the `nf-core/deepmutscan` pipeline to unlock the power of shotgun sequencing based DMS studies on long ORFs, to simplify and standardise the complex bioinformatics steps involved in data processing of such experiments – from read alignment to QC reporting and fitness landscape inferences.\n\n![nf-core/deepmutscan workflow](docs/images/pipeline.png)\n\nThe pipeline is built using [Nextflow](https://www.nextflow.io), a workflow tool to run tasks across multiple compute infrastructures in a very portable manner. It uses Docker/Singularity containers making installation trivial and results highly reproducible. The [Nextflow DSL2](https://www.nextflow.io/docs/latest/dsl2.html) implementation of this pipeline uses one container per process which makes it much easier to maintain and update software dependencies. Where possible, these processes have been submitted to and installed from [nf-core/modules](https://github.com/nf-core/modules) in order to make them available to all nf-core pipelines, and to everyone within the Nextflow community!\n\nOn release, automated continuous integration tests run the pipeline on a full-sized dataset on the AWS cloud infrastructure. This ensures that the pipeline runs on AWS, has sensible resource allocation defaults set to run on real-world datasets, and permits the persistent storage of results to benchmark between pipeline releases and other analysis sources. The results obtained from the full-sized test can be viewed on the [nf-core website](https://nf-co.re/deepmutscan/results).\n\n## Major features\n\n- End-to-end analyses of various DMS data\n- Modular, three-stage workflow: alignment → QC → error-aware fitness estimation\n- Integration with popular statistical fitness estimation tools like [DiMSum](https://github.com/lehner-lab/DiMSum), [Enrich2](https://github.com/FowlerLab/Enrich2), [rosace](https://github.com/pimentellab/rosace/) and [mutscan](https://github.com/fmicompbio/mutscan)\n- Support of multiple mutagenesis strategies, e.g. by nicking with degenerate NNK and NNS codons\n- Containerisation via Docker, Singularity and Apptainer\n- Scalability across HPC and Cloud systems\n- Monitoring of CPU, memory, and CO₂ usage\n\nFor more details on the pipeline and on potential future expansions, please consider reading our [usage description](https://nf-co.re/deepmutscan/usage).\n\n## Step-by-step pipeline summary\n\nThe pipeline processes deep mutational scanning (DMS) sequencing data in several stages:\n\n1. Alignment of reads to the reference open reading frame (ORF) (`BWA-mem`)\n2. Filtering of wildtype and erroneous reads (`samtools view`)\n3. Read merging for base error reduction (`vsearch merge`)\n4. Mutation counting\n5. Single nucleotide variant error correction\n6. DMS library quality control\n7. Data summarisation across samples\n8. Fitness estimation (`DiMSum`, `mutscan`)\n\n## Usage\n\n> [!NOTE]\n> If you are new to Nextflow and nf-core, please refer to [this page](https://nf-co.re/docs/get_started/environment_setup/overview) on how to set-up Nextflow. Make sure to [test your setup](https://nf-co.re/docs/get_started/run-your-first-pipeline) with `-profile test` before running the workflow on actual data.\n\nFirst, prepare a samplesheet with your input/output data in which each row represents a pair of fastq files (paired end). This should look as follows:\n\n```csv title=\"samplesheet.csv\"\nsample,type,replicate,file1,file2\nORF1,input,1,/reads/forward1.fastq.gz,/reads/reverse1.fastq.gz\nORF1,input,2,/reads/forward2.fastq.gz,/reads/reverse2.fastq.gz\nORF1,output,1,/reads/forward3.fastq.gz,/reads/reverse3.fastq.gz\nORF1,output,2,/reads/forward4.fastq.gz,/reads/reverse4.fastq.gz\n```\n\nSecondly, specify the gene or gene region of interest using a reference FASTA file via `--fasta`. Provide the exact codon coordinates using `--reading_frame`.\n\nNow, you can run the pipeline using:\n\n```bash title=\"example_run.sh\"\nnextflow run nf-core/deepmutscan \\\n -profile \\\n --input ./samplesheet.csv \\\n --fasta ./ref.fa \\\n --reading_frame 1-300 \\\n --outdir ./results\n```\n\n## Pipeline output\n\nTo see the results of an example test run with a full size dataset refer to the [results](https://nf-co.re/deepmutscan/results) tab on the nf-core website pipeline page.\n\nFor more details about the output files and reports, please refer to the\n[output documentation](https://nf-co.re/deepmutscan/output).\n\n## Contributing\n\nWe welcome contributions from the community!\n\nFor technical challenges and feedback on the pipeline, please use our [Github repository](https://github.com/nf-core/deepmutscan). Please open an [issue](https://github.com/nf-core/deepmutscan/issues/new) or [pull request](https://github.com/nf-core/deepmutscan/compare) to:\n\n- Report bugs or solve data incompatibilities when running `nf-core/deepmutscan`\n- Suggest the implementation of new modules for custom DMS workflows\n- Help improve this documentation\n\nIf you are interested in getting involved as a developer, please consider joining our interactive [`#deepmutscan` Slack channel](https://nfcore.slack.com/channels/deepmutscan) (via [this invite](https://nf-co.re/join/slack)).\n\n## Credits\n\nnf-core/deepmutscan was originally written by [Benjamin Wehnert](https://github.com/BenjaminWehnert1008) and [Max Stammnitz](https://github.com/MaximilianStammnitz) at the [Centre for Genomic Regulation, Barcelona](https://www.crg.eu/), with the generous support of an EMBO Long-term Postdoctoral Fellowship and a Marie Skłodowska-Curie grant by the European Union.\n\nIf you use `nf-core/deepmutscan` in your analyses, please cite:\n\n> 📄 Wehnert et al., _bioRxiv_ preprint (coming soon)\n\nPlease also cite the `nf-core` framework:\n\n> 📄 Ewels et al., _Nature Biotechnology_, 2020\n> [https://doi.org/10.1038/s41587-020-0439-x](https://doi.org/10.1038/s41587-020-0439-x)\n\nFor further information or help, don't hesitate to get in touch on the [Slack `#deepmutscan` channel](https://nfcore.slack.com/channels/deepmutscan) (you can join with [this invite](https://nf-co.re/join/slack)).\n\n## Scientific contact\n\nFor scientific discussions around the use of this pipeline (e.g. on experimental design or sequencing data requirements), please feel free to get in touch with us directly:\n\n- Benjamin Wehnert — wehnertbenjamin@gmail.com\n- Maximilian Stammnitz — maximilian.stammnitz@crg.eu\n", "hasPart": [ { "@id": "main.nf" @@ -43,6 +43,9 @@ { "@id": "modules/" }, + { + "@id": "modules/local/" + }, { "@id": "modules/nf-core/" }, @@ -99,7 +102,7 @@ }, "mentions": [ { - "@id": "#7786fe09-7f6a-48ce-8926-af918fe3b5b1" + "@id": "#7146010e-cfc6-420d-99f0-b8197ccd79c7" } ], "name": "nf-core/deepmutscan" @@ -121,14 +124,18 @@ }, { "@id": "main.nf", - "@type": ["File", "SoftwareSourceCode", "ComputationalWorkflow"], + "@type": [ + "File", + "SoftwareSourceCode", + "ComputationalWorkflow" + ], "contributor": [ { - "@id": "#f321369f-b518-4a4f-87b7-a49c86013f6e" + "@id": "#72177bf6-a8bc-482d-af8f-8a6c470a26a6" } ], "dateCreated": "", - "dateModified": "2026-04-30T13:32:46Z", + "dateModified": "2026-06-13T20:39:59Z", "dct:conformsTo": "https://bioschemas.org/profiles/ComputationalWorkflow/1.0-RELEASE/", "keywords": [ "nf-core", @@ -138,16 +145,25 @@ "genotype-phenotype", "shotgun-sequencing" ], - "license": ["MIT"], - "name": ["nf-core/deepmutscan"], + "license": [ + "MIT" + ], + "name": [ + "nf-core/deepmutscan" + ], "programmingLanguage": { "@id": "https://w3id.org/workflowhub/workflow-ro-crate#nextflow" }, "sdPublisher": { "@id": "https://nf-co.re/" }, - "url": ["https://github.com/nf-core/deepmutscan", "https://nf-co.re/deepmutscan/1.0.0/"], - "version": ["1.0.0"] + "url": [ + "https://github.com/nf-core/deepmutscan", + "https://nf-co.re/deepmutscan/1.0.0/" + ], + "version": [ + "1.0.0" + ] }, { "@id": "https://w3id.org/workflowhub/workflow-ro-crate#nextflow", @@ -162,11 +178,11 @@ "version": "!>=25.10.4" }, { - "@id": "#7786fe09-7f6a-48ce-8926-af918fe3b5b1", + "@id": "#7146010e-cfc6-420d-99f0-b8197ccd79c7", "@type": "TestSuite", "instance": [ { - "@id": "#54531b61-2c83-48b9-909d-71fe62b8c005" + "@id": "#a6466342-1368-4c50-86fc-cb19fd46c1cc" } ], "mainEntity": { @@ -175,7 +191,7 @@ "name": "Test suite for nf-core/deepmutscan" }, { - "@id": "#54531b61-2c83-48b9-909d-71fe62b8c005", + "@id": "#a6466342-1368-4c50-86fc-cb19fd46c1cc", "@type": "TestInstance", "name": "GitHub Actions workflow for testing nf-core/deepmutscan", "resource": "repos/nf-core/deepmutscan/actions/workflows/nf-test.yml", @@ -217,6 +233,11 @@ "@type": "Dataset", "description": "Modules used by the pipeline" }, + { + "@id": "modules/local/", + "@type": "Dataset", + "description": "Pipeline-specific modules" + }, { "@id": "modules/nf-core/", "@type": "Dataset", @@ -304,9 +325,9 @@ "url": "https://nf-co.re/" }, { - "@id": "#f321369f-b518-4a4f-87b7-a49c86013f6e", + "@id": "#72177bf6-a8bc-482d-af8f-8a6c470a26a6", "@type": "Person", "name": "Benjamin Wehnert & Max Stammnitz" } ] -} +} \ No newline at end of file diff --git a/subworkflows/local/calculate_fitness/main.nf b/subworkflows/local/calculate_fitness/main.nf new file mode 100644 index 0000000..6f7b4c4 --- /dev/null +++ b/subworkflows/local/calculate_fitness/main.nf @@ -0,0 +1,151 @@ +/* +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + IMPORT MODULES +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +*/ +include { MERGE_COUNTS } from '../../../modules/local/fitness/merge_counts/main' +include { EXPDESIGN_FITNESS } from '../../../modules/local/fitness/fitness_experimental_design/main' +include { FIND_SYNONYMOUS_MUTATION } from '../../../modules/local/fitness/find_synonymous_mutation/main' +include { FITNESS_CALCULATION } from '../../../modules/local/fitness/fitness_calculation/main' +include { FITNESS_QC } from '../../../modules/local/fitness/fitness_QC/main' +include { FITNESS_HEATMAP } from '../../../modules/local/fitness/fitness_heatmap/main' +include { RUN_DIMSUM } from '../../../modules/local/fitness/run_dimsum/main' +include { RUN_MUTSCAN } from '../../../modules/local/fitness/run_mutscan/main' + +/* +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + SUBWORKFLOW DEFINITION +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +*/ + +workflow CALCULATE_FITNESS { + + take: + ch_fitness_input // channel: output from COUNTS_TO_FITNESS (grouped by sample/replicate logic) + ch_samplesheet_csv // channel: path to samplesheet csv + ch_fasta // channel: tuple([meta], path(fasta)) + val_reading_frame // value: reading frame param + ch_aa_seq // channel: output from DMSANALYSIS_AASEQ (for heatmap) + + main: + ch_versions = Channel.empty() + + // 1. Group per biological sample and merge counts + ch_fitness_input + .map { meta, tsv -> + def s = meta.sample as String + def id = meta.id as String + def base = s ? (s.replaceFirst(/_(input|output|quality)\d+$/, '')) + : (id?.tokenize('_')?.first()) + tuple(base as String, tuple(meta, tsv)) + } + .groupTuple() + .map { base, pairs -> + // sort by replicate so the staged file order is deterministic (cacheable) + def inputs = pairs.findAll { it[0].type == 'input' }.sort { it[0].replicate }.collect { it[1] } + def outputs = pairs.findAll { it[0].type == 'output' }.sort { it[0].replicate }.collect { it[1] } + tuple([sample: base], inputs, outputs) + } + .filter { smeta, ins, outs -> ins && outs } + .set { ch_fitness_bundled } + + // 2. Merge Counts + MERGE_COUNTS( ch_fitness_bundled ) + ch_versions = ch_versions.mix(MERGE_COUNTS.out.versions) + + // 3. Experimental Design + EXPDESIGN_FITNESS( ch_samplesheet_csv ) + ch_versions = ch_versions.mix(EXPDESIGN_FITNESS.out.versions) + + // 4. Find Synonymous Mutations + // Prepare inputs (broadcast logic) + def ch_fasta_path = ch_fasta.map { it[1] } // strip meta + + FIND_SYNONYMOUS_MUTATION( + MERGE_COUNTS.out.merged_counts, + ch_fasta_path.combine(MERGE_COUNTS.out.merged_counts).map { it[0] }, + val_reading_frame.combine(MERGE_COUNTS.out.merged_counts).map { it[0] } + ) + ch_versions = ch_versions.mix(FIND_SYNONYMOUS_MUTATION.out.versions) + + + // 5. Align Channels for Fitness Calculation + + // Key counts and WT by biological sample name + def ch_counts_keyed_d = MERGE_COUNTS.out.merged_counts + .map { smp, counts -> tuple(smp.sample as String, smp, counts) } + + def ch_wt_keyed_d = FIND_SYNONYMOUS_MUTATION.out.synonymous_wt + .map { smp, wt -> tuple(smp.sample as String, wt) } + + // Join by key + def ch_counts_wt_d = ch_counts_keyed_d.join(ch_wt_keyed_d) + .map { key, smp, counts, wt -> tuple(smp, counts, wt) } + + // Broadcast experimental design + def ch_exp_for_each_d = EXPDESIGN_FITNESS.out.experimental_design + .combine(ch_counts_wt_d) + .map { it[0] } + + // Final aligned channels + def ch_run_counts_d = ch_counts_wt_d.map { smp, counts, wt -> tuple(smp, counts) } + def ch_run_wt_d = ch_counts_wt_d.map { smp, counts, wt -> wt } + def ch_run_exp_d = ch_exp_for_each_d + + // 6. Run Fitness Calculation & QC + FITNESS_CALCULATION( + ch_run_counts_d, + ch_run_exp_d, + ch_run_wt_d + ) + ch_versions = ch_versions.mix(FITNESS_CALCULATION.out.versions) + + FITNESS_QC( FITNESS_CALCULATION.out.fitness_estimation ) + ch_versions = ch_versions.mix(FITNESS_QC.out.versions) + + FITNESS_HEATMAP( + FITNESS_CALCULATION.out.fitness_estimation, + ch_aa_seq + ) + ch_versions = ch_versions.mix(FITNESS_HEATMAP.out.versions) + + // 7. Run DiMSum (optional based on params inside subworkflow or handled by control logic) + // Note: Logic checking for 'params.dimsum' needs to be handled. + // Since subworkflows inherit params, we can check params.dimsum here. + + // DiMSum and mutscan are optional, so their artefact channels start empty and are only filled + // when the tool actually ran - the summary report then simply omits the section. + def ch_dimsum_dir = Channel.empty() + def ch_mutscan_plots = Channel.empty() + + if (params.dimsum) { + RUN_DIMSUM( + ch_run_counts_d, + ch_run_wt_d, + ch_run_exp_d + ) + ch_versions = ch_versions.mix(RUN_DIMSUM.out.versions) + ch_dimsum_dir = RUN_DIMSUM.out.results_dir + } + + // 8. Run Mutscan + if (params.mutscan) { + RUN_MUTSCAN( + ch_run_counts_d, + ch_run_wt_d, + ch_run_exp_d + ) + ch_versions = ch_versions.mix(RUN_MUTSCAN.out.versions) + ch_mutscan_plots = RUN_MUTSCAN.out.qc_plots + } + + emit: + fitness_estimation = FITNESS_CALCULATION.out.fitness_estimation + // artefacts the all-in-one report embeds + fitness_plots = FITNESS_QC.out.counts_corr_pdf + .mix(FITNESS_QC.out.fitness_corr_pdf) + .mix(FITNESS_HEATMAP.out.fitness_heatmap) + mutscan_plots = ch_mutscan_plots + dimsum_dir = ch_dimsum_dir + versions = ch_versions +} diff --git a/subworkflows/local/calculate_fitness/meta.yml b/subworkflows/local/calculate_fitness/meta.yml new file mode 100644 index 0000000..872ba1e --- /dev/null +++ b/subworkflows/local/calculate_fitness/meta.yml @@ -0,0 +1,42 @@ +name: "calculate_fitness" +description: Consolidates variant counts and calculates fitness scores. +keywords: + - "deep mutational scanning" + - "fitness estimation" + - "dms" + - "enrichment scores" +components: + - "merge/counts" + - "fitness/calculation" + - "fitness/heatmap" + - "fitness/qc" + - "run/dimsum" + - "run/mutscan" + - "expdesign/fitness" + - "find/synonymous/mutation" +input: + - ch_fitness_input: + type: channel + description: Variant count channels + - ch_samplesheet_csv: + type: channel + description: Pipeline samplesheet + - ch_fasta: + type: channel + description: Reference sequence channel + - val_reading_frame: + type: value + description: Reading frame parameter + - ch_aa_seq: + type: channel + description: Amino acid sequence channel +output: + - fitness_estimation: + type: channel + description: Output fitness metrics + - versions: + type: channel + description: Software versions +authors: + - "@BenjaminWehnert1008" + - "@MaximilianStammnitz" diff --git a/subworkflows/local/utils_nfcore_deepmutscan_pipeline/main.nf b/subworkflows/local/utils_nfcore_deepmutscan_pipeline/main.nf index 01c0ad7..50e4edb 100644 --- a/subworkflows/local/utils_nfcore_deepmutscan_pipeline/main.nf +++ b/subworkflows/local/utils_nfcore_deepmutscan_pipeline/main.nf @@ -10,8 +10,6 @@ include { UTILS_NFSCHEMA_PLUGIN } from '../../nf-core/utils_nfschema_plugin' include { paramsSummaryMap } from 'plugin/nf-schema' -include { samplesheetToList } from 'plugin/nf-schema' -include { paramsHelp } from 'plugin/nf-schema' include { completionEmail } from '../../nf-core/utils_nfcore_pipeline' include { completionSummary } from '../../nf-core/utils_nfcore_pipeline' include { UTILS_NFCORE_PIPELINE } from '../../nf-core/utils_nfcore_pipeline' @@ -32,13 +30,10 @@ workflow PIPELINE_INITIALISATION { nextflow_cli_args // array: List of positional nextflow CLI args outdir // string: The output directory where the results will be saved input // string: Path to input samplesheet - help // boolean: Display help message and exit - help_full // boolean: Show the full help message - show_hidden // boolean: Show hidden parameters in the help message main: - ch_versions = channel.empty() + ch_versions = Channel.empty() // // Print version and exit if required and dump pipeline parameters to JSON file @@ -82,13 +77,14 @@ workflow PIPELINE_INITIALISATION { UTILS_NFSCHEMA_PLUGIN ( workflow, validate_params, + params.help, + params.help_full, + params.show_hidden, null, - help, - help_full, - show_hidden, before_text, after_text, - command + command, + "${projectDir}/nextflow_schema.json" ) // @@ -107,26 +103,40 @@ workflow PIPELINE_INITIALISATION { // Create channel from input file provided through params.input // - channel - .fromList(samplesheetToList(input, "${projectDir}/assets/schema_input.json")) - .map { - meta, fastq_1, fastq_2 -> - if (!fastq_2) { - return [ meta.id, meta + [ single_end:true ], [ fastq_1 ] ] - } else { - return [ meta.id, meta + [ single_end:false ], [ fastq_1, fastq_2 ] ] - } - } - .groupTuple() - .map { samplesheet -> - validateInputSamplesheet(samplesheet) + Channel + .fromPath(params.input) + .splitCsv(header: true) + .filter { row -> + // Skip rows where file1 or file2 are BAM files + !(row.file1.endsWith('.bam') || (row.file2 && row.file2.endsWith('.bam'))) + } + .map { row -> + // Determine suffix based on the presence of file2 + def suffix = row.file2 ? "_pe" : "_se" + + // Construct metadata object with updated ID + def meta = [ + id : "${row.sample}_${row.type}_${row.replicate}${suffix}", // Base ID with suffix + sample : row.sample, + type : row.type, + replicate : row.replicate as int + ] + + // Generate file paths based on the presence of file1 and file2 + def reads = [] + if (row.file1) { + reads << row.file1 // Add file1 path } - .map { - meta, fastqs -> - return [ meta, fastqs.flatten() ] + if (row.file2) { + reads << row.file2 // Add file2 path } - .set { ch_samplesheet } + // Return metadata and file paths as a tuple + return [meta, reads] + } + .set { ch_samplesheet } + + // Emit the samplesheet channel and an empty version channel for use in the workflow emit: samplesheet = ch_samplesheet versions = ch_versions @@ -232,7 +242,6 @@ def genomeExistsError() { // Generate methods description for MultiQC // def toolCitationText() { - // TODO nf-core: Optionally add in-text citation tools to this list. // Can use ternary operators to dynamically construct based conditions, e.g. params["run_xyz"] ? "Tool (Foo et al. 2023)" : "", // Uncomment function in methodsDescriptionText to render in MultiQC report def citation_text = [ @@ -246,7 +255,6 @@ def toolCitationText() { } def toolBibliographyText() { - // TODO nf-core: Optionally add bibliographic entries to this list. // Can use ternary operators to dynamically construct based conditions, e.g. params["run_xyz"] ? "
  • Author (2023) Pub name, Journal, DOI
  • " : "", // Uncomment function in methodsDescriptionText to render in MultiQC report def reference_text = [ @@ -281,9 +289,8 @@ def methodsDescriptionText(mqc_methods_yaml) { meta["tool_citations"] = "" meta["tool_bibliography"] = "" - // TODO nf-core: Only uncomment below if logic in toolCitationText/toolBibliographyText has been filled! - // meta["tool_citations"] = toolCitationText().replaceAll(", \\.", ".").replaceAll("\\. \\.", ".").replaceAll(", \\.", ".") - // meta["tool_bibliography"] = toolBibliographyText() + meta["tool_citations"] = toolCitationText().replaceAll(", \\.", ".").replaceAll("\\. \\.", ".").replaceAll(", \\.", ".") + meta["tool_bibliography"] = toolBibliographyText() def methods_text = mqc_methods_yaml.text diff --git a/subworkflows/local/utils_nfcore_deepmutscan_pipeline/meta.yml b/subworkflows/local/utils_nfcore_deepmutscan_pipeline/meta.yml new file mode 100644 index 0000000..2f5669a --- /dev/null +++ b/subworkflows/local/utils_nfcore_deepmutscan_pipeline/meta.yml @@ -0,0 +1,58 @@ +name: "PIPELINE_INITIALISATION" +description: Local utility functions, pipeline initialization, completion workflows, and parameter validation wrappers for the deepmutscan pipeline. +keywords: + - "utility" + - "validation" + - "manifest" + - "initialisation" + - "completion" +components: + - "utils_nfschema_plugin" + - "utils_nfcore_pipeline" + - "utils_nextflow_pipeline" + - "completionemail" + - "completionsummary" + +input: + - version: + type: boolean + description: Display version and exit + - validate_params: + type: boolean + description: Boolean whether to validate parameters against the schema at runtime + - monochrome_logs: + type: boolean + description: Do not use coloured log outputs + - nextflow_cli_args: + type: array + description: List of positional nextflow CLI args + - outdir: + type: string + description: The output directory where the results will be saved + - input: + type: string + description: Path to input samplesheet + - email: + type: string + description: Email address for completion summary + - email_on_fail: + type: string + description: Email address sent on pipeline failure + - plaintext_email: + type: boolean + description: Send plain-text email instead of HTML + - multiqc_report: + type: string + description: Path to MultiQC report + +output: + - samplesheet: + type: channel + description: Processed samplesheet channel containing metadata objects and file paths + - versions: + type: channel + description: Empty version channel for use in the workflow + +authors: + - "@BenjaminWehnert1008" + - "@MaximilianStammnitz" diff --git a/subworkflows/nf-core/utils_nextflow_pipeline/main.nf b/subworkflows/nf-core/utils_nextflow_pipeline/main.nf index d6e593e..37939ac 100644 --- a/subworkflows/nf-core/utils_nextflow_pipeline/main.nf +++ b/subworkflows/nf-core/utils_nextflow_pipeline/main.nf @@ -73,11 +73,23 @@ def getWorkflowVersion() { def dumpParametersToJSON(outdir) { def timestamp = new java.util.Date().format('yyyy-MM-dd_HH-mm-ss') def filename = "params_${timestamp}.json" - def temp_pf = new File(workflow.launchDir.toString(), ".${filename}") - def jsonStr = groovy.json.JsonOutput.toJson(params) + def temp_pf = workflow.launchDir.resolve(".${filename}") + def jsonGenerator = new groovy.json.JsonGenerator.Options() + .excludeNulls() + .addConverter(Path) { Path path -> path.toUriString() } + .addConverter(Duration) { Duration duration -> duration.toMillis() } + .addConverter(MemoryUnit) { MemoryUnit memory -> memory.toBytes() } + .addConverter(nextflow.script.types.VersionNumber) { nextflow.script.types.VersionNumber version -> version.toString() } + .build() + def jsonStr = jsonGenerator.toJson(params) temp_pf.text = groovy.json.JsonOutput.prettyPrint(jsonStr) - - nextflow.extension.FilesEx.copyTo(temp_pf.toPath(), "${outdir}/pipeline_info/params_${timestamp}.json") + if (outdir instanceof Path) { + temp_pf.copyTo(outdir.resolve("pipeline_info/${filename}")) + } else if (outdir instanceof String) { + temp_pf.copyTo("${outdir}/pipeline_info/params_${timestamp}.json") + } else { + log.warn("Could not determine type of outdir, parameters JSON file will not be copied to output directory!") + } temp_pf.delete() } diff --git a/subworkflows/nf-core/utils_nfcore_pipeline/tests/main.workflow.nf.test b/subworkflows/nf-core/utils_nfcore_pipeline/tests/main.workflow.nf.test deleted file mode 100644 index 8940d32..0000000 --- a/subworkflows/nf-core/utils_nfcore_pipeline/tests/main.workflow.nf.test +++ /dev/null @@ -1,29 +0,0 @@ -nextflow_workflow { - - name "Test Workflow UTILS_NFCORE_PIPELINE" - script "../main.nf" - config "subworkflows/nf-core/utils_nfcore_pipeline/tests/nextflow.config" - workflow "UTILS_NFCORE_PIPELINE" - tag "subworkflows" - tag "subworkflows_nfcore" - tag "utils_nfcore_pipeline" - tag "subworkflows/utils_nfcore_pipeline" - - test("Should run without failures") { - - when { - workflow { - """ - input[0] = [] - """ - } - } - - then { - assertAll( - { assert workflow.success }, - { assert snapshot(workflow.out).match() } - ) - } - } -} diff --git a/subworkflows/nf-core/utils_nfcore_pipeline/tests/main.workflow.nf.test.snap b/subworkflows/nf-core/utils_nfcore_pipeline/tests/main.workflow.nf.test.snap deleted file mode 100644 index 859d103..0000000 --- a/subworkflows/nf-core/utils_nfcore_pipeline/tests/main.workflow.nf.test.snap +++ /dev/null @@ -1,19 +0,0 @@ -{ - "Should run without failures": { - "content": [ - { - "0": [ - true - ], - "valid_config": [ - true - ] - } - ], - "meta": { - "nf-test": "0.8.4", - "nextflow": "23.10.1" - }, - "timestamp": "2024-02-28T12:03:25.726491" - } -} \ No newline at end of file diff --git a/subworkflows/nf-core/utils_nfschema_plugin/main.nf b/subworkflows/nf-core/utils_nfschema_plugin/main.nf index 1df8b76..9ff0681 100644 --- a/subworkflows/nf-core/utils_nfschema_plugin/main.nf +++ b/subworkflows/nf-core/utils_nfschema_plugin/main.nf @@ -22,6 +22,7 @@ workflow UTILS_NFSCHEMA_PLUGIN { before_text // string: text to show before the help message and parameters summary after_text // string: text to show after the help message and parameters summary command // string: an example command of the pipeline + cli_typecast // boolean: whether to perform typecasting of CLI parameters. Set this to `null` to use the default behaviour main: @@ -34,11 +35,11 @@ workflow UTILS_NFSCHEMA_PLUGIN { fullHelp: help_full, ] if(parameters_schema) { - help_options << [parametersSchema: parameters_schema] + help_options << [parameters_schema: parameters_schema] } log.info paramsHelp( help_options, - (params.help instanceof String && params.help != "true") ? params.help : "", + (help instanceof String && help != "true") ? help : "", ) exit 0 } @@ -50,7 +51,7 @@ workflow UTILS_NFSCHEMA_PLUGIN { summary_options = [:] if(parameters_schema) { - summary_options << [parametersSchema: parameters_schema] + summary_options << [parameters_schema: parameters_schema] } log.info before_text log.info paramsSummaryLog(summary_options, input_workflow) @@ -63,7 +64,10 @@ workflow UTILS_NFSCHEMA_PLUGIN { if(validate_params) { validateOptions = [:] if(parameters_schema) { - validateOptions << [parametersSchema: parameters_schema] + validateOptions << [parameters_schema: parameters_schema] + } + if(cli_typecast != null) { + validateOptions << [cast_cli_params: cli_typecast] } validateParameters(validateOptions) } diff --git a/subworkflows/nf-core/utils_nfschema_plugin/meta.yml b/subworkflows/nf-core/utils_nfschema_plugin/meta.yml index f7d9f02..1d8c75a 100644 --- a/subworkflows/nf-core/utils_nfschema_plugin/meta.yml +++ b/subworkflows/nf-core/utils_nfschema_plugin/meta.yml @@ -25,6 +25,30 @@ input: option. When this input is empty it will automatically use the configured schema or "${projectDir}/nextflow_schema.json" as default. The schema should not be given in this way for meta pipelines. + - help: + type: boolean, string + description: | + Show the help message and exit. When a parameter name is given, show the help message for that parameter instead of the general help message. + - help_full: + type: boolean + description: Show the full help message and exit. + - show_hidden: + type: boolean + description: Show hidden parameters in the help message. + - before_text: + type: string + description: Text to show before the parameters summary and help message. + - after_text: + type: string + description: Text to show after the parameters summary and help message. + - command: + type: string + description: An example command to run the pipeline, to show in the help message and the summary. + - cli_typecast: + type: boolean + description: | + Whether to apply typecasting to the parameters given via the CLI before validation. + Set this to `null` to use the default behavior. output: - dummy_emit: type: boolean diff --git a/subworkflows/nf-core/utils_nfschema_plugin/tests/main.nf.test b/subworkflows/nf-core/utils_nfschema_plugin/tests/main.nf.test index c977917..1fd1eac 100644 --- a/subworkflows/nf-core/utils_nfschema_plugin/tests/main.nf.test +++ b/subworkflows/nf-core/utils_nfschema_plugin/tests/main.nf.test @@ -31,6 +31,7 @@ nextflow_workflow { input[6] = "" input[7] = "" input[8] = "" + input[9] = null """ } } @@ -63,6 +64,7 @@ nextflow_workflow { input[6] = "" input[7] = "" input[8] = "" + input[9] = null """ } } @@ -95,6 +97,7 @@ nextflow_workflow { input[6] = "" input[7] = "" input[8] = "" + input[9] = null """ } } @@ -127,6 +130,7 @@ nextflow_workflow { input[6] = "" input[7] = "" input[8] = "" + input[9] = null """ } } @@ -160,6 +164,7 @@ nextflow_workflow { input[6] = "Before" input[7] = "After" input[8] = "nextflow run test/test" + input[9] = null """ } } diff --git a/subworkflows/nf-core/utils_nfschema_plugin/tests/nextflow.config b/subworkflows/nf-core/utils_nfschema_plugin/tests/nextflow.config index f6537cc..fd71cb8 100644 --- a/subworkflows/nf-core/utils_nfschema_plugin/tests/nextflow.config +++ b/subworkflows/nf-core/utils_nfschema_plugin/tests/nextflow.config @@ -1,5 +1,5 @@ plugins { - id "nf-schema@2.6.1" + id "nf-schema@2.7.2" } validation { diff --git a/tests/.nftignore b/tests/.nftignore index e128a12..0c8b5c9 100644 --- a/tests/.nftignore +++ b/tests/.nftignore @@ -10,3 +10,26 @@ multiqc/multiqc_plots/{svg,pdf,png}/*.{svg,pdf,png} multiqc/multiqc_report.html fastqc/*_fastqc.{html,zip} pipeline_info/*.{html,json,txt,yml} +library_QC/**/*.pdf +fitness/DiMSum_results/**/*.pdf +fitness/DiMSum_results/**/dimsum_results_workspace.RData +fitness/DiMSum_results/**/report.html +fitness/DiMSum_results/**/*.png +**/*.pdf +**/*.RData +**/*_error_correction_report.html +**/variant_effect_inspection_tool.html +deepmutscan_report.html +**/structure_data.json +fitness/DiMSum_results/**/report.html +**/fitness_estimation_mutscan_edgeR.tsv +**/fitness_estimation_mutscan_limma.tsv +**/dimsum_results_*_merge.tsv +**/fitness_synonymous.txt +**/*_fitness_input.tsv +**/counts_merged.tsv +**/fitness_estimation.tsv +**/*.variant_counts.tsv +**/annotated_variantCounts.csv +**/library_completed_variantCounts*.csv +**/variantCounts_filtered_by_library*.csv diff --git a/tests/default.nf.test b/tests/default.nf.test index 6187c32..2fa68e0 100644 --- a/tests/default.nf.test +++ b/tests/default.nf.test @@ -14,9 +14,15 @@ nextflow_pipeline { then { // stable_path: All files + folders in ${params.outdir}/ with a stable path (including file name) - def stable_path = getAllFilesFromDir(params.outdir, relative: true, includeDir: true, ignore: ['pipeline_info/*.{html,json,txt}']) + // DiMSum's diagnostic plots are excluded from the path list as well as from the content + // hashes (tests/.nftignore): which of them DiMSum emits depends on its graphics stack, so + // their mere presence is not a stable contract to assert on. + def stable_path = getAllFilesFromDir(params.outdir, relative: true, includeDir: true, ignore: ['pipeline_info/*.{html,json,txt}', 'multiqc/multiqc_plots', 'multiqc/multiqc_plots/**', 'fitness/DiMSum_results/**/reports/*.{pdf,png}']) // stable_content: All files in ${params.outdir}/ with stable content def stable_content = getAllFilesFromDir(params.outdir, ignoreFile: 'tests/.nftignore') + // Parse the unstable TSVs to snapshot their structure instead of their hashes + def edger_tsv = path("$outputDir/fitness/mutscan_results/fitness_estimation_mutscan_edgeR.tsv").csv(sep: '\t') + def limma_tsv = path("$outputDir/fitness/mutscan_results/fitness_estimation_mutscan_limma.tsv").csv(sep: '\t') assert workflow.success assertAll( { assert snapshot( @@ -25,7 +31,12 @@ nextflow_pipeline { // All stable path name, with a relative path stable_path, // All files with stable contents - stable_content + stable_content, + // Partial structural snapshot of the unstable TSV files + [ + edger_rowCount: edger_tsv.rowCount, + limma_rowCount: limma_tsv.rowCount + ] ).match() } ) } diff --git a/tests/default.nf.test.snap b/tests/default.nf.test.snap new file mode 100644 index 0000000..c8ecb88 --- /dev/null +++ b/tests/default.nf.test.snap @@ -0,0 +1,390 @@ +{ + "-profile test": { + "content": [ + { + "BWA_INDEX": { + "bwa": "0.7.19-r1273" + }, + "BWA_MEM": { + "bwa": "0.7.19-r1273", + "samtools": "1.22.1" + }, + "DIMSUM_RUN": { + "r-base": "4.4.2" + }, + "DMSANALYSIS_AASEQ": { + "r-base": "4.5.1", + "r-Biostrings": "2.78.0" + }, + "DMSANALYSIS_ERROR_CORRECTION_FALSE_DOUBLES": { + "r-base": "4.5.1", + "r-stringr": "1.6.0", + "r-Biostrings": "2.78.0" + }, + "DMSANALYSIS_POSSIBLE_MUTATIONS": { + "r-base": "4.5.1", + "biostrings": "2.78.0" + }, + "EXPDESIGN_FITNESS": { + "r-base": "4.5.1" + }, + "FASTQC": { + "fastqc": "0.12.1" + }, + "FIND_SYNONYMOUS_MUTATION": { + "r-base": "4.5.1", + "r-Biostrings": "2.78.0" + }, + "FITNESS_CALCULATION": { + "r-base": "4.5.1", + "r-Biostrings": "2.78.0" + }, + "FITNESS_HEATMAP": { + "r-base": "4.5.1", + "r-dplyr": "1.1.4", + "r-ggplot2": "4.0.2", + "r-methods": "4.5.1", + "r-grid": "4.5.1" + }, + "FITNESS_QC": { + "r-base": "4.5.1" + }, + "MERGE_COUNTS": { + "r-base": "4.5.1" + }, + "RUN_MUTSCAN": { + "r-base": "4.5.1", + "r-mutscan": "1.0.0", + "r-Biostrings": "2.78.0" + }, + "SAMTOOLS_INDEX": { + "samtools": "1.23.1" + }, + "SAMTOOLS_SORT": { + "samtools": "1.23.1" + }, + "VARIANTCOUNTING": { + "python": "3.12.13", + "pysam": "0.24.0", + "polars": "1.33.1", + "pyarrow": "24.0.0", + "numpy": "2.5.1", + "biopython": 1.87 + }, + "VARIANT_EFFECT_INSPECTION_TOOL": { + "python": "3.12.3", + "pandas": "2.2.1", + "numpy": "1.26.4" + }, + "VISUALIZATION_ERROR_CORRECTION_REPORT": { + "python": "3.12.3", + "pandas": "2.2.1", + "numpy": "1.26.4" + }, + "VISUALIZATION_SEQDEPTH": { + "r-base": "4.5.1", + "r-ggplot2": "4.0.2" + }, + "Workflow": { + "nf-core/deepmutscan": "v1.0.0" + } + }, + [ + "deepmutscan_report.html", + "fastqc", + "fastqc/GID1A_input_1_pe_1_fastqc.html", + "fastqc/GID1A_input_1_pe_1_fastqc.zip", + "fastqc/GID1A_input_1_pe_2_fastqc.html", + "fastqc/GID1A_input_1_pe_2_fastqc.zip", + "fastqc/GID1A_input_2_pe_1_fastqc.html", + "fastqc/GID1A_input_2_pe_1_fastqc.zip", + "fastqc/GID1A_input_2_pe_2_fastqc.html", + "fastqc/GID1A_input_2_pe_2_fastqc.zip", + "fastqc/GID1A_output_1_pe_1_fastqc.html", + "fastqc/GID1A_output_1_pe_1_fastqc.zip", + "fastqc/GID1A_output_1_pe_2_fastqc.html", + "fastqc/GID1A_output_1_pe_2_fastqc.zip", + "fastqc/GID1A_output_2_pe_1_fastqc.html", + "fastqc/GID1A_output_2_pe_1_fastqc.zip", + "fastqc/GID1A_output_2_pe_2_fastqc.html", + "fastqc/GID1A_output_2_pe_2_fastqc.zip", + "fitness", + "fitness/DiMSum_results", + "fitness/DiMSum_results/dimsum_results", + "fitness/DiMSum_results/dimsum_results/dimsum_results_fitness_intermediate.RData", + "fitness/DiMSum_results/dimsum_results/dimsum_results_fitness_replicates.RData", + "fitness/DiMSum_results/dimsum_results/dimsum_results_indel_variant_data_merge.tsv", + "fitness/DiMSum_results/dimsum_results/dimsum_results_nobarcode_variant_data_merge.tsv", + "fitness/DiMSum_results/dimsum_results/dimsum_results_rejected_variant_data_merge.tsv", + "fitness/DiMSum_results/dimsum_results/dimsum_results_sessionInfo.RData", + "fitness/DiMSum_results/dimsum_results/dimsum_results_variant_data_merge.RData", + "fitness/DiMSum_results/dimsum_results/dimsum_results_variant_data_merge.tsv", + "fitness/DiMSum_results/dimsum_results/dimsum_results_workspace.RData", + "fitness/DiMSum_results/dimsum_results/fitness_doubles.txt", + "fitness/DiMSum_results/dimsum_results/fitness_singles.txt", + "fitness/DiMSum_results/dimsum_results/fitness_singles_MaveDB.csv", + "fitness/DiMSum_results/dimsum_results/fitness_synonymous.txt", + "fitness/DiMSum_results/dimsum_results/fitness_wildtype.txt", + "fitness/DiMSum_results/dimsum_results/report.html", + "fitness/DiMSum_results/dimsum_results/reports", + "fitness/DiMSum_results/dimsum_results/reports/report.Rmd", + "fitness/DiMSum_results/dimsum_results/reports/report_settings.RData", + "fitness/DiMSum_results/dimsum_results/tmp", + "fitness/DiMSum_results/dimsum_results/tmp/mutation_stats_dicts.RData", + "fitness/DiMSum_results/single_rep_counts", + "fitness/DiMSum_results/single_rep_counts/GID1A_input_1_pe_fitness_input.tsv", + "fitness/DiMSum_results/single_rep_counts/GID1A_input_2_pe_fitness_input.tsv", + "fitness/DiMSum_results/single_rep_counts/GID1A_output_1_pe_fitness_input.tsv", + "fitness/DiMSum_results/single_rep_counts/GID1A_output_2_pe_fitness_input.tsv", + "fitness/counts_merged.tsv", + "fitness/default_results", + "fitness/default_results/fitness_estimation.tsv", + "fitness/default_results/fitness_estimation_count_correlation.pdf", + "fitness/default_results/fitness_estimation_fitness_correlation.pdf", + "fitness/default_results/fitness_heatmap.pdf", + "fitness/experimentalDesign.tsv", + "fitness/mutscan_results", + "fitness/mutscan_results/fitness_estimation_mutscan_edgeR.tsv", + "fitness/mutscan_results/fitness_estimation_mutscan_limma.tsv", + "fitness/mutscan_results/mutscan_counts_corr.pdf", + "fitness/mutscan_results/mutscan_edgeR_volcano.pdf", + "fitness/mutscan_results/mutscan_limma_volcano.pdf", + "fitness/synonymous_wt.txt", + "intermediate_files", + "intermediate_files/aa_seq.txt", + "intermediate_files/bam_files", + "intermediate_files/bam_files/bwa", + "intermediate_files/bam_files/bwa/mem", + "intermediate_files/bam_files/bwa/mem/GID1A_input_1_pe.bam", + "intermediate_files/bam_files/bwa/mem/GID1A_input_2_pe.bam", + "intermediate_files/bam_files/bwa/mem/GID1A_output_1_pe.bam", + "intermediate_files/bam_files/bwa/mem/GID1A_output_2_pe.bam", + "intermediate_files/bam_files/filtered", + "intermediate_files/bam_files/filtered/GID1A_input_1_pe_filtered.bam", + "intermediate_files/bam_files/filtered/GID1A_input_2_pe_filtered.bam", + "intermediate_files/bam_files/filtered/GID1A_output_1_pe_filtered.bam", + "intermediate_files/bam_files/filtered/GID1A_output_2_pe_filtered.bam", + "intermediate_files/bam_files/premerged", + "intermediate_files/bam_files/premerged/GID1A_input_1_pe_merged.bam", + "intermediate_files/bam_files/premerged/GID1A_input_2_pe_merged.bam", + "intermediate_files/bam_files/premerged/GID1A_output_1_pe_merged.bam", + "intermediate_files/bam_files/premerged/GID1A_output_2_pe_merged.bam", + "intermediate_files/bam_files/sorted", + "intermediate_files/bam_files/sorted/GID1A_input_1_pe.sorted.bam", + "intermediate_files/bam_files/sorted/GID1A_input_1_pe.sorted.bam.bai", + "intermediate_files/bam_files/sorted/GID1A_input_2_pe.sorted.bam", + "intermediate_files/bam_files/sorted/GID1A_input_2_pe.sorted.bam.bai", + "intermediate_files/bam_files/sorted/GID1A_output_1_pe.sorted.bam", + "intermediate_files/bam_files/sorted/GID1A_output_1_pe.sorted.bam.bai", + "intermediate_files/bam_files/sorted/GID1A_output_2_pe.sorted.bam", + "intermediate_files/bam_files/sorted/GID1A_output_2_pe.sorted.bam.bai", + "intermediate_files/bwa", + "intermediate_files/bwa/GID1A.amb", + "intermediate_files/bwa/GID1A.ann", + "intermediate_files/bwa/GID1A.bwt", + "intermediate_files/bwa/GID1A.pac", + "intermediate_files/bwa/GID1A.sa", + "intermediate_files/possible_mutations.csv", + "intermediate_files/processed_variant_counts", + "intermediate_files/processed_variant_counts/GID1A_input_1_pe", + "intermediate_files/processed_variant_counts/GID1A_input_1_pe/annotated_variantCounts.csv", + "intermediate_files/processed_variant_counts/GID1A_input_1_pe/library_completed_variantCounts.csv", + "intermediate_files/processed_variant_counts/GID1A_input_1_pe/library_completed_variantCounts_error_corrected.csv", + "intermediate_files/processed_variant_counts/GID1A_input_1_pe/seq_error_rate.csv", + "intermediate_files/processed_variant_counts/GID1A_input_1_pe/variantCounts_filtered_by_library.csv", + "intermediate_files/processed_variant_counts/GID1A_input_1_pe/variantCounts_filtered_by_library_error_corrected.csv", + "intermediate_files/processed_variant_counts/GID1A_input_1_pe/variantCounts_for_heatmaps.csv", + "intermediate_files/processed_variant_counts/GID1A_input_1_pe/variantCounts_for_heatmaps_error_corrected.csv", + "intermediate_files/processed_variant_counts/GID1A_input_2_pe", + "intermediate_files/processed_variant_counts/GID1A_input_2_pe/annotated_variantCounts.csv", + "intermediate_files/processed_variant_counts/GID1A_input_2_pe/library_completed_variantCounts.csv", + "intermediate_files/processed_variant_counts/GID1A_input_2_pe/library_completed_variantCounts_error_corrected.csv", + "intermediate_files/processed_variant_counts/GID1A_input_2_pe/seq_error_rate.csv", + "intermediate_files/processed_variant_counts/GID1A_input_2_pe/variantCounts_filtered_by_library.csv", + "intermediate_files/processed_variant_counts/GID1A_input_2_pe/variantCounts_filtered_by_library_error_corrected.csv", + "intermediate_files/processed_variant_counts/GID1A_input_2_pe/variantCounts_for_heatmaps.csv", + "intermediate_files/processed_variant_counts/GID1A_input_2_pe/variantCounts_for_heatmaps_error_corrected.csv", + "intermediate_files/processed_variant_counts/GID1A_output_1_pe", + "intermediate_files/processed_variant_counts/GID1A_output_1_pe/annotated_variantCounts.csv", + "intermediate_files/processed_variant_counts/GID1A_output_1_pe/library_completed_variantCounts.csv", + "intermediate_files/processed_variant_counts/GID1A_output_1_pe/library_completed_variantCounts_error_corrected.csv", + "intermediate_files/processed_variant_counts/GID1A_output_1_pe/seq_error_rate.csv", + "intermediate_files/processed_variant_counts/GID1A_output_1_pe/variantCounts_filtered_by_library.csv", + "intermediate_files/processed_variant_counts/GID1A_output_1_pe/variantCounts_filtered_by_library_error_corrected.csv", + "intermediate_files/processed_variant_counts/GID1A_output_1_pe/variantCounts_for_heatmaps.csv", + "intermediate_files/processed_variant_counts/GID1A_output_1_pe/variantCounts_for_heatmaps_error_corrected.csv", + "intermediate_files/processed_variant_counts/GID1A_output_2_pe", + "intermediate_files/processed_variant_counts/GID1A_output_2_pe/annotated_variantCounts.csv", + "intermediate_files/processed_variant_counts/GID1A_output_2_pe/library_completed_variantCounts.csv", + "intermediate_files/processed_variant_counts/GID1A_output_2_pe/library_completed_variantCounts_error_corrected.csv", + "intermediate_files/processed_variant_counts/GID1A_output_2_pe/seq_error_rate.csv", + "intermediate_files/processed_variant_counts/GID1A_output_2_pe/variantCounts_filtered_by_library.csv", + "intermediate_files/processed_variant_counts/GID1A_output_2_pe/variantCounts_filtered_by_library_error_corrected.csv", + "intermediate_files/processed_variant_counts/GID1A_output_2_pe/variantCounts_for_heatmaps.csv", + "intermediate_files/processed_variant_counts/GID1A_output_2_pe/variantCounts_for_heatmaps_error_corrected.csv", + "intermediate_files/variant_counts", + "intermediate_files/variant_counts/GID1A_input_1_pe", + "intermediate_files/variant_counts/GID1A_input_1_pe/GID1A_input_1_pe.variant_counts.tsv", + "intermediate_files/variant_counts/GID1A_input_1_pe/variant_counts_columns.tsv", + "intermediate_files/variant_counts/GID1A_input_2_pe", + "intermediate_files/variant_counts/GID1A_input_2_pe/GID1A_input_2_pe.variant_counts.tsv", + "intermediate_files/variant_counts/GID1A_input_2_pe/variant_counts_columns.tsv", + "intermediate_files/variant_counts/GID1A_output_1_pe", + "intermediate_files/variant_counts/GID1A_output_1_pe/GID1A_output_1_pe.variant_counts.tsv", + "intermediate_files/variant_counts/GID1A_output_1_pe/variant_counts_columns.tsv", + "intermediate_files/variant_counts/GID1A_output_2_pe", + "intermediate_files/variant_counts/GID1A_output_2_pe/GID1A_output_2_pe.variant_counts.tsv", + "intermediate_files/variant_counts/GID1A_output_2_pe/variant_counts_columns.tsv", + "library_QC", + "library_QC/GID1A_input_1_pe", + "library_QC/GID1A_input_1_pe/SeqDepth.pdf", + "library_QC/GID1A_input_1_pe/counts_heatmap.pdf", + "library_QC/GID1A_input_1_pe/counts_per_cov_heatmap.pdf", + "library_QC/GID1A_input_1_pe/logdiff_plot.pdf", + "library_QC/GID1A_input_1_pe/logdiff_varying_bases.pdf", + "library_QC/GID1A_input_1_pe/rolling_counts.pdf", + "library_QC/GID1A_input_1_pe/rolling_counts_per_cov.pdf", + "library_QC/GID1A_input_1_pe/rolling_coverage.pdf", + "library_QC/GID1A_input_1_pe/seqdepth_curve.csv", + "library_QC/GID1A_input_2_pe", + "library_QC/GID1A_input_2_pe/SeqDepth.pdf", + "library_QC/GID1A_input_2_pe/counts_heatmap.pdf", + "library_QC/GID1A_input_2_pe/counts_per_cov_heatmap.pdf", + "library_QC/GID1A_input_2_pe/logdiff_plot.pdf", + "library_QC/GID1A_input_2_pe/logdiff_varying_bases.pdf", + "library_QC/GID1A_input_2_pe/rolling_counts.pdf", + "library_QC/GID1A_input_2_pe/rolling_counts_per_cov.pdf", + "library_QC/GID1A_input_2_pe/rolling_coverage.pdf", + "library_QC/GID1A_input_2_pe/seqdepth_curve.csv", + "library_QC/GID1A_output_1_pe", + "library_QC/GID1A_output_1_pe/SeqDepth.pdf", + "library_QC/GID1A_output_1_pe/counts_heatmap.pdf", + "library_QC/GID1A_output_1_pe/counts_per_cov_heatmap.pdf", + "library_QC/GID1A_output_1_pe/logdiff_plot.pdf", + "library_QC/GID1A_output_1_pe/logdiff_varying_bases.pdf", + "library_QC/GID1A_output_1_pe/rolling_counts.pdf", + "library_QC/GID1A_output_1_pe/rolling_counts_per_cov.pdf", + "library_QC/GID1A_output_1_pe/rolling_coverage.pdf", + "library_QC/GID1A_output_1_pe/seqdepth_curve.csv", + "library_QC/GID1A_output_2_pe", + "library_QC/GID1A_output_2_pe/SeqDepth.pdf", + "library_QC/GID1A_output_2_pe/counts_heatmap.pdf", + "library_QC/GID1A_output_2_pe/counts_per_cov_heatmap.pdf", + "library_QC/GID1A_output_2_pe/logdiff_plot.pdf", + "library_QC/GID1A_output_2_pe/logdiff_varying_bases.pdf", + "library_QC/GID1A_output_2_pe/rolling_counts.pdf", + "library_QC/GID1A_output_2_pe/rolling_counts_per_cov.pdf", + "library_QC/GID1A_output_2_pe/rolling_coverage.pdf", + "library_QC/GID1A_output_2_pe/seqdepth_curve.csv", + "library_QC/error_correction_report.html", + "multiqc", + "multiqc/multiqc_data", + "multiqc/multiqc_data/fastqc-status-check-heatmap.txt", + "multiqc/multiqc_data/fastqc_adapter_content_plot.txt", + "multiqc/multiqc_data/fastqc_overrepresented_sequences_plot.txt", + "multiqc/multiqc_data/fastqc_per_base_n_content_plot.txt", + "multiqc/multiqc_data/fastqc_per_base_sequence_quality_plot.txt", + "multiqc/multiqc_data/fastqc_per_sequence_gc_content_plot_Counts.txt", + "multiqc/multiqc_data/fastqc_per_sequence_gc_content_plot_Percentages.txt", + "multiqc/multiqc_data/fastqc_per_sequence_quality_scores_plot.txt", + "multiqc/multiqc_data/fastqc_sequence_counts_plot.txt", + "multiqc/multiqc_data/fastqc_sequence_duplication_levels_plot.txt", + "multiqc/multiqc_data/fastqc_top_overrepresented_sequences_table.txt", + "multiqc/multiqc_data/llms-full.txt", + "multiqc/multiqc_data/multiqc.log", + "multiqc/multiqc_data/multiqc.parquet", + "multiqc/multiqc_data/multiqc_citations.txt", + "multiqc/multiqc_data/multiqc_data.json", + "multiqc/multiqc_data/multiqc_fastqc.txt", + "multiqc/multiqc_data/multiqc_general_stats.txt", + "multiqc/multiqc_data/multiqc_software_versions.txt", + "multiqc/multiqc_data/multiqc_sources.txt", + "multiqc/multiqc_report.html", + "pipeline_info", + "pipeline_info/nf_core_deepmutscan_software_mqc_versions.yml", + "variant_effect_inspection_tool", + "variant_effect_inspection_tool/structure_data.json", + "variant_effect_inspection_tool/variant_effect_inspection_tool.html" + ], + [ + "fitness_doubles.txt:md5,68b329da9893e34099c7d8ad5cb9c940", + "fitness_singles.txt:md5,df4effcdf5477327ab7e74034498ddc8", + "fitness_singles_MaveDB.csv:md5,160ad250f0774e770b5128840b62ba80", + "fitness_wildtype.txt:md5,934b2d1b64746b4130d99e7bd46d5595", + "report.Rmd:md5,a19acae87f16d0059c6699bc1e818d95", + "experimentalDesign.tsv:md5,f95dea4a3e6cf7e206e789a96e502604", + "synonymous_wt.txt:md5,bb94c5975185b80f8d63acebfbf4a0da", + "aa_seq.txt:md5,421287586f566191f7da4a7aad7d24a8", + "GID1A_input_1_pe.bam:md5,57f54fa2bad68dc8ff39d2bb79d94742", + "GID1A_input_2_pe.bam:md5,5da708382cddfaa90e7b53e3d79d8f8e", + "GID1A_output_1_pe.bam:md5,f888077ed2234b2b7b033ceea621b33f", + "GID1A_output_2_pe.bam:md5,5b147ba80d87ee3ff3be6c3f34d7f67d", + "GID1A_input_1_pe_filtered.bam:md5,9a8f3691ec0478928227fe0da590054b", + "GID1A_input_2_pe_filtered.bam:md5,50f50c79514041c8746fb95070fbf321", + "GID1A_output_1_pe_filtered.bam:md5,b743b1fd86941c392c7d42474a82217d", + "GID1A_output_2_pe_filtered.bam:md5,20e02c4c5f0ba2ff9f9b553e80a89343", + "GID1A_input_1_pe_merged.bam:md5,e83e32f077c1f8018dd7cda8a27828fd", + "GID1A_input_2_pe_merged.bam:md5,eeb0a34e585e1858d3b758d6bde3b8db", + "GID1A_output_1_pe_merged.bam:md5,ed034511d33464cd9f032b8824663842", + "GID1A_output_2_pe_merged.bam:md5,9bafd9eee748877ac559815b1420b865", + "GID1A_input_1_pe.sorted.bam:md5,80311313f5d8b1387130ba376bd8f2b9", + "GID1A_input_1_pe.sorted.bam.bai:md5,644255e89a4f621627ee67d5884fe79e", + "GID1A_input_2_pe.sorted.bam:md5,084adf0c95308ceef7eba177999ed6fe", + "GID1A_input_2_pe.sorted.bam.bai:md5,8f5580680b4aa02ea4800f422d366a8b", + "GID1A_output_1_pe.sorted.bam:md5,95a6016ac42a49925b3300ecb57465f3", + "GID1A_output_1_pe.sorted.bam.bai:md5,38e8757743fca3dbd3b1f44c1cb49475", + "GID1A_output_2_pe.sorted.bam:md5,73b5c65751b796b6565ba18b316935ab", + "GID1A_output_2_pe.sorted.bam.bai:md5,f032b0115c80e79b05eb757bd75445e1", + "GID1A.amb:md5,383ef17f6abb846ab669ebac30f5659b", + "GID1A.ann:md5,30c4a2204f9166d9594c790e0ca58793", + "GID1A.bwt:md5,120149e0239465625c2dd20a171701fd", + "GID1A.pac:md5,104f743d81d39b2527b5ae52db26654c", + "GID1A.sa:md5,1f697dcb3fedd2f3f29642598bdd795b", + "possible_mutations.csv:md5,9ce59fe91b726b1d88587a13fa899567", + "seq_error_rate.csv:md5,350a018c981eca2506ac6291a6e9e161", + "variantCounts_for_heatmaps.csv:md5,842bd5c00760d594e2a450aec929e4aa", + "variantCounts_for_heatmaps_error_corrected.csv:md5,842bd5c00760d594e2a450aec929e4aa", + "seq_error_rate.csv:md5,350a018c981eca2506ac6291a6e9e161", + "variantCounts_for_heatmaps.csv:md5,44069d5abca76323ccaed3c182447104", + "variantCounts_for_heatmaps_error_corrected.csv:md5,44069d5abca76323ccaed3c182447104", + "seq_error_rate.csv:md5,350a018c981eca2506ac6291a6e9e161", + "variantCounts_for_heatmaps.csv:md5,3a9ce900a567cb663bc71033d3771d8a", + "variantCounts_for_heatmaps_error_corrected.csv:md5,3a9ce900a567cb663bc71033d3771d8a", + "seq_error_rate.csv:md5,350a018c981eca2506ac6291a6e9e161", + "variantCounts_for_heatmaps.csv:md5,f263448ec0d7ec68f6e23aa050b961e6", + "variantCounts_for_heatmaps_error_corrected.csv:md5,f263448ec0d7ec68f6e23aa050b961e6", + "variant_counts_columns.tsv:md5,c94801255a553ce5ff4a00d3170f6768", + "variant_counts_columns.tsv:md5,c94801255a553ce5ff4a00d3170f6768", + "variant_counts_columns.tsv:md5,c94801255a553ce5ff4a00d3170f6768", + "variant_counts_columns.tsv:md5,c94801255a553ce5ff4a00d3170f6768", + "seqdepth_curve.csv:md5,eceecf3eff21b17b1d818c6ccefa72d9", + "seqdepth_curve.csv:md5,04649f2dd97ae3f7f4d7a94efb299d78", + "seqdepth_curve.csv:md5,5bde4b2a2b0cce57f4696913f4cdf8e7", + "seqdepth_curve.csv:md5,0deb002959055376128c81bcfba8e0e2", + "error_correction_report.html:md5,45455144c768473a8242381380deeba8", + "fastqc-status-check-heatmap.txt:md5,58a9580c11af4210b1af65753383e120", + "fastqc_adapter_content_plot.txt:md5,2990ad34b6fa29151dd4b9a38828afbe", + "fastqc_overrepresented_sequences_plot.txt:md5,c703a9df0ea3a5c1c5f515a1fd88bbe8", + "fastqc_per_base_n_content_plot.txt:md5,6af419871cdec77149cf013b72e82417", + "fastqc_per_base_sequence_quality_plot.txt:md5,460b8e65cf8d58087b3143ba78b30e17", + "fastqc_per_sequence_gc_content_plot_Counts.txt:md5,7f1eacaa19e912e18a4419b23e749e3a", + "fastqc_per_sequence_gc_content_plot_Percentages.txt:md5,0e405d9394ae8aeaa6ae162e18f7c195", + "fastqc_per_sequence_quality_scores_plot.txt:md5,5d1ee338c146901ca9f1690f7ee47200", + "fastqc_sequence_counts_plot.txt:md5,8b438078b7f3475b6ce659cc4b8dca11", + "fastqc_sequence_duplication_levels_plot.txt:md5,a0839f96103959fd7265cbb986e2724a", + "multiqc_citations.txt:md5,4c806e63a283ec1b7e78cdae3a923d4f", + "multiqc_fastqc.txt:md5,72cb1ee38d354d674ced1b4b21bbeff4", + "multiqc_general_stats.txt:md5,7c2fd44ded06835128a208d3365aa23b" + ], + { + "edger_rowCount": 1231, + "limma_rowCount": 1231 + } + ], + "timestamp": "2026-07-22T20:08:15.261389", + "meta": { + "nf-test": "0.9.4", + "nextflow": "26.04.1" + } + } +} \ No newline at end of file diff --git a/tests/nextflow.config b/tests/nextflow.config index bcd98b2..b4075e0 100644 --- a/tests/nextflow.config +++ b/tests/nextflow.config @@ -4,11 +4,26 @@ ======================================================================================== */ -// TODO nf-core: Specify any additional parameters here -// Or any resources requirements +process { + resourceLimits = [ + cpus: 4, + memory: '8.GB', + time: '1.h' + ] +} + params { modules_testdata_base_path = 'https://raw.githubusercontent.com/nf-core/test-datasets/modules/data/' pipelines_testdata_base_path = 'https://raw.githubusercontent.com/nf-core/test-datasets/refs/heads/deepmutscan/' + + // Input data + input = params.pipelines_testdata_base_path + 'samplesheet/GID1A_test.csv' + fasta = params.pipelines_testdata_base_path + 'testdata/GID1A.fasta' + reading_frame = '352-1383' + min_counts = 2 + mutagenesis_type = 'nnk_nns' + run_seqdepth = true + fitness = true } aws.client.anonymous = true // fixes S3 access issues on self-hosted runners diff --git a/workflows/deepmutscan.nf b/workflows/deepmutscan.nf index aaafe3f..36ed769 100644 --- a/workflows/deepmutscan.nf +++ b/workflows/deepmutscan.nf @@ -5,6 +5,31 @@ */ include { FASTQC } from '../modules/nf-core/fastqc/main' include { MULTIQC } from '../modules/nf-core/multiqc/main' +include { BWA_INDEX } from '../modules/nf-core/bwa/index/main' +include { BWA_MEM } from '../modules/nf-core/bwa/mem/main' +include { BAMFILTER_DMS } from '../modules/local/bamprocessing/bam_filter/main' +include { PREMERGE } from '../modules/local/bamprocessing/premerge/main' +include { SAMTOOLS_SORT } from '../modules/nf-core/samtools/sort/main' +include { SAMTOOLS_INDEX } from '../modules/nf-core/samtools/index/main' +include { VARIANTCOUNTING } from '../modules/local/variantcounting/main' +include { DMSANALYSIS_AASEQ } from '../modules/local/dmsanalysis/aa_seq/main' +include { DMSANALYSIS_POSSIBLE_MUTATIONS } from '../modules/local/dmsanalysis/possible_mutations/main' +include { DMSANALYSIS_PROCESS_VARIANT_COUNTS } from '../modules/local/dmsanalysis/process_variant_counts/main' +include { DMSANALYSIS_ERROR_CORRECTION_FALSE_DOUBLES } from '../modules/local/dmsanalysis/error_correction_false_doubles/main' +include { DMSANALYSIS_ERROR_CORRECTION_WILDTYPE } from '../modules/local/dmsanalysis/error_correction_wildtype/main' +include { VISUALIZATION_COUNTS_PER_COV } from '../modules/local/visualization/counts_per_cov/main' +include { VISUALIZATION_COUNTS_HEATMAP } from '../modules/local/visualization/counts_heatmap/main' +include { VISUALIZATION_GLOBAL_POS_BIASES_COUNTS } from '../modules/local/visualization/global_pos_biases_counts/main' +include { VISUALIZATION_GLOBAL_POS_BIASES_COV } from '../modules/local/visualization/global_pos_biases_cov/main' +include { VISUALIZATION_LOGDIFF } from '../modules/local/visualization/logdiff/main' +include { VISUALIZATION_SEQDEPTH } from '../modules/local/visualization/seqdepth/main' +include { VISUALIZATION_ERROR_CORRECTION_REPORT } from '../modules/local/visualization/error_correction_report/main' +include { VISUALIZATION_SUMMARY_REPORT } from '../modules/local/visualization/summary_report/main' +include { VARIANT_EFFECT_INSPECTION_TOOL } from '../modules/local/structure/variant_effect_inspection_tool/main' +include { COUNTS_TO_FITNESS } from '../modules/local/fitness/counts_to_fitness/main' + +include { CALCULATE_FITNESS } from '../subworkflows/local/calculate_fitness/main' + include { paramsSummaryMap } from 'plugin/nf-schema' include { paramsSummaryMultiqc } from '../subworkflows/nf-core/utils_nfcore_pipeline' include { softwareVersionsToYAML } from '../subworkflows/nf-core/utils_nfcore_pipeline' @@ -27,14 +52,380 @@ workflow DEEPMUTSCAN { main: - def ch_versions = channel.empty() - def ch_multiqc_files = channel.empty() + def ch_versions = Channel.empty() + def ch_multiqc_files = Channel.empty() + + // Define input channels from parameters + def ch_fasta = Channel + .fromPath(params.fasta, checkIfExists: true) + .map { fasta -> tuple( [id: 'ref'], fasta ) } + + def reading_frame_ch = Channel.value(params.reading_frame) + def min_counts_ch = Channel.value(params.min_counts) + def custom_codon_library_ch = Channel.value(params.custom_codon_library) + def mutagenesis_type_ch = Channel.value(params.mutagenesis_type) + def sliding_window_size_ch = Channel.value(params.sliding_window_size) + def aimed_cov_ch = Channel.value(params.aimed_cov) + def run_seqdepth_ch = Channel.value(params.run_seqdepth) + def base_qual_ch = Channel.value(params.base_qual) + def min_flank_ch = Channel.value(params.min_flank) + + // Raw samplesheet path channel for downstream subworkflows + def ch_samplesheet_csv = Channel.fromPath(params.input, checkIfExists: true) + + // '--dimsum' only has an effect together with '--fitness' + if (params.dimsum && !params.fitness) { + log.warn("'--dimsum true' only works together with '--fitness true'. DiMSum will be skipped.") + } + + // '--error_correction wildtype' needs a 'wildtype' sample for every biological sample + if (params.error_correction == 'wildtype') { + ch_samplesheet + .map { meta, _reads -> tuple(meta.sample as String, meta.type as String) } + .toList() + .map { pairs -> + pairs.groupBy { it[0] }.each { sample, rows -> + def types = rows.collect { it[1] } + if (types.any { it != 'wildtype' } && !types.contains('wildtype')) { + error( + "[--error_correction wildtype] No 'wildtype' sample found for '${sample}' in the samplesheet.\n" + + " Add a samplesheet row with type 'wildtype' and sample '${sample}' (deep wildtype-only sequencing),\n" + + " or use the internal correction instead (default: --error_correction false_doubles),\n" + + " or disable error correction with --error_correction none." + ) + } + } + pairs + } + .subscribe { } + } + + // Nudge fitness users who did not supply a structure towards the interactive tool + if (params.fitness && !params.pdb) { + log.info( + "\n" + + " ┌─ Optional: interactive variant effect inspection tool ───────────────────┐\n" + + " Supply a wildtype structure with '--pdb ' to also build an\n" + + " interactive HTML tool that projects fitness, counts and error-correction\n" + + " biases onto the 3D structure to support data interpretation.\n" + + " └───────────────────────────────────────────────────────────────────────────┘\n" + ) + } + // // MODULE: Run FastQC // FASTQC(ch_samplesheet) ch_multiqc_files = ch_multiqc_files.mix(FASTQC.out.zip.map{ _meta, file -> file }) + // + // MODULE: BWA Index + // + BWA_INDEX ( + ch_fasta + ) + + // Broadcast index to all samples + def ch_bwa_index = BWA_INDEX.out.index + + // Broadcast the index to all samples + def ch_bwa_index_broadcast = ch_samplesheet + .combine(ch_bwa_index) + .map { [it[2], it[3]] } + + // Broadcast the fasta to all samples + def ch_fasta_broadcast = ch_fasta + .combine(ch_samplesheet) + .map { [it[0], it[1]] } + + // Broadcast the sort flag to all samples + def ch_sort_bam = ch_samplesheet.map { false } + + // Run BWA_MEM with all four inputs aligned + BWA_MEM( + ch_samplesheet, + ch_bwa_index_broadcast, + ch_fasta_broadcast, + ch_sort_bam + ) + + BAMFILTER_DMS ( + BWA_MEM.out.bam + ) + + // Broadcast the FASTA path to every BAM emitted by BAMFILTER_DMS + def ch_fasta_path_broadcast = ch_fasta + .combine(BAMFILTER_DMS.out.bam) // flattened item: [meta3, fasta, meta, bam] + .map { it[1] } // keep only the fasta path (N emissions) + + PREMERGE( + BAMFILTER_DMS.out.bam, // tuple(val(meta), path(bam)) + ch_fasta_path_broadcast // path(fasta) + ) + + // Coordinate-sort + index the premerged BAM (required by the read-based counter) + SAMTOOLS_SORT( + PREMERGE.out.bam, // tuple(val(meta), path(bam)) + PREMERGE.out.bam.map { _meta, _bam -> [ [:], [], [] ] }, // empty fasta tuple (BAM sort) + Channel.value('') // index_format '' -> index separately + ) + SAMTOOLS_INDEX(SAMTOOLS_SORT.out.bam) + + // Join sorted BAM with its index -> tuple(val(meta), path(bam), path(bai)) + def ch_sorted_indexed = SAMTOOLS_SORT.out.bam.join(SAMTOOLS_INDEX.out.index) + + // Broadcast singletons to N (one per sample), anchored on the sorted+indexed channel + def ch_fasta_for_counts = ch_fasta.combine(ch_sorted_indexed).map { it[1] } // path -- N + def ch_rf_for_counts = reading_frame_ch.combine(ch_sorted_indexed).map { it[0] } // val -- N + def ch_min_for_counts = min_counts_ch.combine(ch_sorted_indexed).map { it[0] } // val -- N + def ch_bq_for_counts = base_qual_ch.combine(ch_sorted_indexed).map { it[0] } // val -- N + def ch_flank_for_counts = min_flank_ch.combine(ch_sorted_indexed).map { it[0] } // val -- N + + VARIANTCOUNTING( + ch_sorted_indexed, // tuple(val(meta), path(bam), path(bai)) + ch_fasta_for_counts, // path(fasta) + ch_rf_for_counts, // val(reading_frame string, 1-based inclusive) + ch_min_for_counts, // val(min_counts) + ch_bq_for_counts, // val(base_qual) + ch_flank_for_counts // val(min_flank) + ) + ch_versions = ch_versions.mix(VARIANTCOUNTING.out.versions) + + DMSANALYSIS_AASEQ ( + ch_fasta, + reading_frame_ch + ) + ch_versions = ch_versions.mix(DMSANALYSIS_AASEQ.out.versions) + + DMSANALYSIS_POSSIBLE_MUTATIONS( + ch_fasta, + reading_frame_ch, // pos_range (as val) + mutagenesis_type_ch, // mutagenesis_type (as val) + custom_codon_library_ch // custom_codon_library (as path) + ) + ch_versions = ch_versions.mix(DMSANALYSIS_POSSIBLE_MUTATIONS.out.versions) + + // Anchor (N items; one per sample) + def ch_vc = VARIANTCOUNTING.out.variant_counts // tuple(val(meta), path) + + // Build per-sample inputs using inline combinations (replaces fanout) + def ch_possible_mut_for_proc = DMSANALYSIS_POSSIBLE_MUTATIONS.out.possible_mutations.map { it[1] }.combine(ch_vc).map { it[0] } + def ch_aa_seq_for_proc = DMSANALYSIS_AASEQ.out.aa_seq.map { it[1] }.combine(ch_vc).map { it[0] } + def ch_min_counts_for_proc = min_counts_ch.combine(ch_vc).map { it[0] } + + // Call with all inputs aligned (each has N items now) + DMSANALYSIS_PROCESS_VARIANT_COUNTS( + ch_vc, // tuple(val(meta), path(variantCounts)) -- N + ch_possible_mut_for_proc, // path(possible_mutations) -- N + ch_aa_seq_for_proc, // path(aa_seq) -- N + ch_min_counts_for_proc // val(min_counts) -- N + ) + + def annotated_variantCounts_ch = DMSANALYSIS_PROCESS_VARIANT_COUNTS.out.processed_variantCounts.map { meta, a, b, c, d -> tuple(meta, a) } + def variantCounts_filtered_by_library_ch = DMSANALYSIS_PROCESS_VARIANT_COUNTS.out.processed_variantCounts.map { meta, a, b, c, d -> tuple(meta, b) } + def library_completed_variantCounts_ch = DMSANALYSIS_PROCESS_VARIANT_COUNTS.out.processed_variantCounts.map { meta, a, b, c, d -> tuple(meta, c) } + def variantCounts_for_heatmaps_ch = DMSANALYSIS_PROCESS_VARIANT_COUNTS.out.processed_variantCounts.map { meta, a, b, c, d -> tuple(meta, d) } + + // + // Sequencing-error correction of single-codon counts (default: false_doubles). + // Replaces the filtered + heatmap channels with corrected versions; downstream fitness + // picks up correction via the `counts_corrected` column, count heatmaps via canonical names. + // + if (params.error_correction == 'false_doubles') { + // per sample: raw (pre-filter) counts + library-filtered counts + completed library table + def ch_fd_in = ch_vc.join(variantCounts_filtered_by_library_ch).join(library_completed_variantCounts_ch) // (meta, raw, filtered, completed) + def aa_seq_for_ec = DMSANALYSIS_AASEQ.out.aa_seq.map { it[1] }.combine(ch_fd_in).map { it[0] } + def min_for_ec = min_counts_ch.combine(ch_fd_in).map { it[0] } + // the reference the reads were aligned to (base_mut is in this fasta's coordinate frame), + // broadcast one-per-sample; the estimator and the mode/window are run-level constants. + // ch_fasta is tuple(meta, fasta), so the path is it[1] - it[0] is the [id:ref] meta map. + def fasta_for_ec = ch_fasta.combine(ch_fd_in).map { it[1] } + DMSANALYSIS_ERROR_CORRECTION_FALSE_DOUBLES( + ch_fd_in, fasta_for_ec, aa_seq_for_ec, min_for_ec, + params.false_doubles_method, params.false_doubles_codon_window + ) + ch_versions = ch_versions.mix(DMSANALYSIS_ERROR_CORRECTION_FALSE_DOUBLES.out.versions) + variantCounts_filtered_by_library_ch = DMSANALYSIS_ERROR_CORRECTION_FALSE_DOUBLES.out.corrected.map { meta, filt, heat, comp -> tuple(meta, filt) } + variantCounts_for_heatmaps_ch = DMSANALYSIS_ERROR_CORRECTION_FALSE_DOUBLES.out.corrected.map { meta, filt, heat, comp -> tuple(meta, heat) } + library_completed_variantCounts_ch = DMSANALYSIS_ERROR_CORRECTION_FALSE_DOUBLES.out.corrected.map { meta, filt, heat, comp -> tuple(meta, comp) } + } + else if (params.error_correction == 'wildtype') { + // pair each non-wildtype sample with the wildtype sample of the same `sample` base + def filt_comp = variantCounts_filtered_by_library_ch.join(library_completed_variantCounts_ch) // (meta, filtered, completed) + def wt_ch = filt_comp.filter { meta, f, c -> meta.type == 'wildtype' }.map { meta, f, c -> tuple(meta.sample, f) } + def tgt_ch = filt_comp.filter { meta, f, c -> meta.type != 'wildtype' }.map { meta, f, c -> tuple(meta.sample, meta, f, c) } + def ch_wt_in = tgt_ch.combine(wt_ch, by: 0).map { _base, meta, filt, comp, wtf -> tuple(meta, filt, wtf, comp) } // (meta, filtered, wt_filtered, completed) + def aa_seq_for_ec = DMSANALYSIS_AASEQ.out.aa_seq.map { it[1] }.combine(ch_wt_in).map { it[0] } + def min_for_ec = min_counts_ch.combine(ch_wt_in).map { it[0] } + DMSANALYSIS_ERROR_CORRECTION_WILDTYPE( ch_wt_in, aa_seq_for_ec, min_for_ec ) + ch_versions = ch_versions.mix(DMSANALYSIS_ERROR_CORRECTION_WILDTYPE.out.versions) + variantCounts_filtered_by_library_ch = DMSANALYSIS_ERROR_CORRECTION_WILDTYPE.out.corrected.map { meta, filt, heat, comp -> tuple(meta, filt) } + variantCounts_for_heatmaps_ch = DMSANALYSIS_ERROR_CORRECTION_WILDTYPE.out.corrected.map { meta, filt, heat, comp -> tuple(meta, heat) } + library_completed_variantCounts_ch = DMSANALYSIS_ERROR_CORRECTION_WILDTYPE.out.corrected.map { meta, filt, heat, comp -> tuple(meta, comp) } + } + // else 'none': keep the uncorrected channels + + // Artefacts the all-in-one report embeds, collected as [[kind, group, name], file]. Optional + // parts simply never mix anything in, and the report drops the corresponding section. + def ch_report_assets = Channel.empty() + + // + // MODULE: per-sample error-correction report (only when correction was applied) + // + if (params.error_correction != 'none') { + // One run-level report over every file. Sorting first keeps the id list and the staged files in + // the same order, which is the only thing tying a column of numbers to the file it came from. + def ch_ec = variantCounts_filtered_by_library_ch.toSortedList { a, b -> a[0].id <=> b[0].id } + + VISUALIZATION_ERROR_CORRECTION_REPORT( + ch_ec.map { rows -> rows[0][0].sample ?: 'run' }, + ch_ec.map { rows -> groovy.json.JsonOutput.toJson(rows.collect { [id: it[0].id, type: it[0].type, replicate: it[0].replicate] }).bytes.encodeBase64().toString() }, + ch_ec.map { rows -> rows.collect { it[1] } }, + params.error_correction, + params.false_doubles_method, + file("${projectDir}/assets/error_correction_report/ec_report_template.html", checkIfExists: true) + ) + ch_versions = ch_versions.mix(VISUALIZATION_ERROR_CORRECTION_REPORT.out.versions) + ch_report_assets = ch_report_assets.mix( + VISUALIZATION_ERROR_CORRECTION_REPORT.out.report.map { f -> [['report', 'ec', 'Error correction'], f] } + ) + } + + // --- For VISUALIZATION_COUNTS_PER_COV & HEATMAP (replaces fanoutTo) + def min_counts_for_cov_ch = min_counts_ch.combine(variantCounts_for_heatmaps_ch).map { it[0] } + def min_counts_for_heatmap_ch = min_counts_ch.combine(variantCounts_for_heatmaps_ch).map { it[0] } + + // --- For VISUALIZATION_GLOBAL_POS_BIASES_* + def aa_seq_for_bias_ch = DMSANALYSIS_AASEQ.out.aa_seq.map { it[1] }.combine(variantCounts_filtered_by_library_ch).map { it[0] } + def sliding_window_size_N = sliding_window_size_ch.combine(variantCounts_filtered_by_library_ch).map { it[0] } + def aimed_cov_N = aimed_cov_ch.combine(variantCounts_filtered_by_library_ch).map { it[0] } + + // --- For VISUALIZATION_SEQDEPTH + def possible_mutations_N = DMSANALYSIS_POSSIBLE_MUTATIONS.out.possible_mutations.map { it[1] }.combine(variantCounts_filtered_by_library_ch).map { it[0] } + def min_counts_for_seqdepth_ch = min_counts_ch.combine(variantCounts_filtered_by_library_ch).map { it[0] } + + VISUALIZATION_COUNTS_PER_COV( + variantCounts_for_heatmaps_ch, + min_counts_for_cov_ch + ) + + VISUALIZATION_COUNTS_HEATMAP( + variantCounts_for_heatmaps_ch, + min_counts_for_heatmap_ch + ) + + VISUALIZATION_GLOBAL_POS_BIASES_COUNTS( + variantCounts_filtered_by_library_ch, + aa_seq_for_bias_ch, + sliding_window_size_N + ) + + VISUALIZATION_GLOBAL_POS_BIASES_COV( + variantCounts_filtered_by_library_ch, + aa_seq_for_bias_ch, + sliding_window_size_N, + aimed_cov_N + ) + + VISUALIZATION_LOGDIFF( + library_completed_variantCounts_ch + ) + + // library QC plots (original R PDFs) + the tables the report re-plots interactively + ch_report_assets = ch_report_assets + .mix(VISUALIZATION_GLOBAL_POS_BIASES_COV.out.rolling_coverage.map { meta, f -> [['qc', meta.id, 'rolling_coverage.pdf'], f] }) + .mix(VISUALIZATION_GLOBAL_POS_BIASES_COUNTS.out.rolling_counts.map { meta, f -> [['qc', meta.id, 'rolling_counts.pdf'], f] }) + .mix(VISUALIZATION_GLOBAL_POS_BIASES_COUNTS.out.rolling_counts_per_cov.map { meta, f -> [['qc', meta.id, 'rolling_counts_per_cov.pdf'], f] }) + .mix(VISUALIZATION_COUNTS_HEATMAP.out.counts_heatmap.map { meta, f -> [['qc', meta.id, 'counts_heatmap.pdf'], f] }) + .mix(VISUALIZATION_COUNTS_PER_COV.out.counts_per_cov_heatmap.map { meta, f -> [['qc', meta.id, 'counts_per_cov_heatmap.pdf'], f] }) + .mix(VISUALIZATION_LOGDIFF.out.logdiff_plot.map { meta, f -> [['qc', meta.id, 'logdiff_plot.pdf'], f] }) + .mix(VISUALIZATION_LOGDIFF.out.logdiff_varying_bases.map { meta, f -> [['qc', meta.id, 'logdiff_varying_bases.pdf'], f] }) + .mix(variantCounts_filtered_by_library_ch.map { meta, f -> [['filtered', meta.id, 'counts.csv'], f] }) + .mix(variantCounts_for_heatmaps_ch.map { meta, f -> [['heatmap', meta.id, 'heatmap.csv'], f] }) + // Fixes the ORF axis at the wildtype length: positions with no library coverage still have to + // occupy a slot, or every rolling window downstream is computed over the wrong neighbourhood. + .mix(DMSANALYSIS_AASEQ.out.aa_seq.map { _meta, f -> [['aa_seq', 'run', 'aa_seq.txt'], f] }) + // Needed to name the exact nucleotide change behind each heatmap cell: the fitness table gives + // the variant ORFs, and the wildtype to diff them against only exists in the reference. + .mix(ch_fasta.map { _meta, f -> [['fasta', 'run', 'reference.fasta'], f] }) + + if (params.run_seqdepth) { + VISUALIZATION_SEQDEPTH( + variantCounts_filtered_by_library_ch, + possible_mutations_N, + min_counts_for_seqdepth_ch + ) + ch_versions = ch_versions.mix(VISUALIZATION_SEQDEPTH.out.versions) + // The rarefaction curve travels to the summary report, which re-plots it per file under + // Sequencing QC (the PDF is still written to the results folder for the record). + ch_report_assets = ch_report_assets.mix( + VISUALIZATION_SEQDEPTH.out.curve.map { meta, f -> [['seqdepth', meta.id, 'seqdepth.csv'], f] } + ) + } + + // Broadcast singletons to N (one per sample), anchored on variantCounts_filtered_by_library_ch + def ch_fasta_for_fitness = ch_fasta.combine(variantCounts_filtered_by_library_ch).map { it[1] } // path(fasta) -- N + def ch_rf_for_fitness = reading_frame_ch.combine(variantCounts_filtered_by_library_ch).map { it[0] } // val(range) -- N + + // Call with aligned inputs + COUNTS_TO_FITNESS( + variantCounts_filtered_by_library_ch, // tuple(val(meta), path) + ch_fasta_for_fitness, // path(fasta) + ch_rf_for_fitness // val(reading_frame) + ) + + // Execution of fitness subworkflow, if --fitness true + if (params.fitness) { + + CALCULATE_FITNESS ( + COUNTS_TO_FITNESS.out.fitness_input, // Input from previous step + ch_samplesheet_csv, // Path to samplesheet + ch_fasta, // The original Fasta tuple + reading_frame_ch, // Reading frame value channel + DMSANALYSIS_AASEQ.out.aa_seq // Amino Acid Sequence (for Heatmap) + ) + + // Collect versions + ch_versions = ch_versions.mix(CALCULATE_FITNESS.out.versions) + + ch_report_assets = ch_report_assets + .mix(CALCULATE_FITNESS.out.fitness_estimation.first().map { _m, f -> [['fitness_table', 'default', 'fitness_estimation.tsv'], f] }) + .mix(CALCULATE_FITNESS.out.fitness_plots.map { _m, f -> [['fitness_pdf', 'default', f.name], f] }) + // mutscan emits all of its plots as one list, so flatten to a file per entry + .mix(CALCULATE_FITNESS.out.mutscan_plots.transpose().map { _m, f -> [['mutscan_pdf', 'mutscan', f.name], f] }) + .mix(CALCULATE_FITNESS.out.dimsum_dir.flatten().filter { it.name == 'report.html' }.map { f -> [['report', 'dimsum', 'DiMSum'], f] }) + + // + // MODULE: interactive variant effect inspection tool (one self-contained HTML per sample). + // Runs only when a wildtype structure is supplied via --pdb. Projects fitness, counts and + // error-correction biases onto the 3D structure. (Structure prediction is not wired here.) + // + if (params.pdb) { + def ch_structure = Channel + .fromPath(params.pdb, checkIfExists: true) + .map { pdb -> tuple([id: 'wildtype'], pdb) } + def ch_viewer_input = CALCULATE_FITNESS.out.fitness_estimation + .combine(ch_structure) + .map { smeta, fitness_tsv, _stmeta, pdb -> tuple(smeta, pdb, fitness_tsv) } + VARIANT_EFFECT_INSPECTION_TOOL( + ch_viewer_input, + variantCounts_filtered_by_library_ch.map { _meta, file -> file }.collect(), + DMSANALYSIS_AASEQ.out.aa_seq.map { _meta, file -> file }.first(), + file("${projectDir}/assets/variant_effect_inspection_tool/3Dmol-min.js", checkIfExists: true), + file("${projectDir}/assets/variant_effect_inspection_tool/viewer_template.html", checkIfExists: true), + ) + ch_versions = ch_versions.mix(VARIANT_EFFECT_INSPECTION_TOOL.out.versions) + ch_report_assets = ch_report_assets.mix( + VARIANT_EFFECT_INSPECTION_TOOL.out.viewer.map { _meta, f -> [['report', 'viewer', 'Variant effect inspection tool'], f] } + ) + // The raw structure also travels to the summary report, which builds its own slow-spinning + // fitness-coloured viewer on the Variant effects page from it. + ch_report_assets = ch_report_assets.mix( + ch_structure.map { _meta, pdb -> [['pdb', 'structure', 'structure.pdb'], pdb] } + ) + } + } + // // Collate and save software versions // @@ -76,6 +467,7 @@ workflow DEEPMUTSCAN { : file("${projectDir}/assets/methods_description_template.yml", checkIfExists: true) def ch_methods_description = channel.value(methodsDescriptionText(ch_multiqc_custom_methods_description)) ch_multiqc_files = ch_multiqc_files.mix(ch_methods_description.collectFile(name: 'methods_description_mqc.yaml', sort: true)) + MULTIQC( ch_multiqc_files.flatten().collect().map { files -> [ @@ -90,8 +482,81 @@ workflow DEEPMUTSCAN { ] } ) - emit:multiqc_report = MULTIQC.out.report.map { _meta, report -> [report] }.toList() // channel: /path/to/multiqc_report.html + + // + // MODULE: the single all-in-one report. Runs last, because it embeds every other artefact - + // the MultiQC report included - as a base64 data URI, so the file stands alone when shared. + // + ch_report_assets = ch_report_assets.mix( + MULTIQC.out.report.map { _meta, f -> [['report', 'multiqc', 'MultiQC'], f] } + ) + // The collated software versions drive the Citation page: it cites only the tools this run used. + ch_report_assets = ch_report_assets.mix( + ch_collated_versions.map { f -> [['versions', 'run', 'versions.yml'], f] } + ) + // Nextflow's own execution trace drives the Overview run-statistics table. It is still being + // appended to as the report is built, so it captures every compute-heavy task (all but the report + // task itself) - the page labels it as of report generation. checkIfExists is off because the file + // is engine-managed, not a process output. + ch_report_assets = ch_report_assets.mix( + Channel.fromPath("${outdir}/pipeline_info/execution_trace_${params.trace_report_suffix}.txt", checkIfExists: false) + .map { f -> [['trace', 'run', 'execution_trace.txt'], f] } + ) + + // Sorting keeps the manifest and the staged files in one deterministic, matching order. + def ch_summary_input = ch_report_assets + .toSortedList { a, b -> a[0].join('|') <=> b[0].join('|') } + .map { rows -> + def manifest = rows.collect { [kind: it[0][0], group: it[0][1], name: it[0][2]] } + tuple( + groovy.json.JsonOutput.toJson(manifest).bytes.encodeBase64().toString(), + rows.collect { it[1] }, + ) + } + + def ch_run_meta = variantCounts_filtered_by_library_ch + .first() + .map { meta, _f -> + tuple( + [id: 'run', sample: meta.sample], + groovy.json.JsonOutput.toJson([ + sample: meta.sample, + reading_frame: params.reading_frame, + mutagenesis_type: params.mutagenesis_type, + error_correction: params.error_correction, + version: workflow.manifest.version, + // The report smooths client-side with the same window the R plots use, and draws + // the same required-coverage reference line, so both must travel with the run. + sliding_window_size: params.sliding_window_size, + aimed_cov: params.aimed_cov, + fitness: params.fitness, + dimsum: params.dimsum, + mutscan: params.mutscan, + outdir: params.outdir, + // Lets the Overview link straight to Nextflow's own execution report + timeline, + // which sit next to this file as execution_{report,timeline}_.html. + trace_report_suffix: params.trace_report_suffix, + false_doubles_method: params.false_doubles_method, + ]).bytes.encodeBase64().toString(), + ) + } + + VISUALIZATION_SUMMARY_REPORT( + ch_run_meta, + ch_summary_input.map { manifest, _files -> manifest }, + ch_summary_input.map { _manifest, files -> files }, + file("${projectDir}/assets/summary_report/summary_report_template.html", checkIfExists: true), + file("${projectDir}/assets/variant_effect_inspection_tool/3Dmol-min.js", checkIfExists: true), + ) + ch_versions = ch_versions.mix(VISUALIZATION_SUMMARY_REPORT.out.versions) + + emit: + multiqc_report = MULTIQC.out.report.map { _meta, report -> [report] }.toList() // channel: /path/to/multiqc_report.html versions = ch_versions // channel: [ path(versions.yml) ] + bwa_index = BWA_INDEX.out.index + aligned_bam = BWA_MEM.out.bam + filtered_bam = BAMFILTER_DMS.out.bam + premerged_bam = PREMERGE.out.bam } /*